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 } else if (is_kanji1(s, sp - s)) {
266 (void) sprintf(op, "%c%02X", quotechar, *sp);
279 * quotechar lead-in character used to quote illegal characters as
282 * callerbuf buffer to house result
283 * bufsz size of callerbuf
286 fname_decode(quotechar, s, callerbuf, bufsz)
292 int k, calc, cnt = 0;
293 static char hexdigits[] = "0123456789ABCDEF";
301 /* Do we have room for one more character? */
302 if ((bufsz - cnt) <= 2)
304 if (*sp == quotechar) {
306 for (k = 0; k < 16; ++k)
307 if (*sp == hexdigits[k])
310 return callerbuf; /* impossible, so bail */
313 for (k = 0; k < 16; ++k)
314 if (*sp == hexdigits[k])
317 return callerbuf; /* impossible, so bail */
331 #ifdef PREFIXES_IN_USE
332 #define UNUSED_if_not_PREFIXES_IN_USE /*empty*/
334 #define UNUSED_if_not_PREFIXES_IN_USE UNUSED
339 fqname(basenam, whichprefix, buffnum)
341 int whichprefix UNUSED_if_not_PREFIXES_IN_USE;
342 int buffnum UNUSED_if_not_PREFIXES_IN_USE;
344 #ifndef PREFIXES_IN_USE
347 if (!basenam || whichprefix < 0 || whichprefix >= PREFIX_COUNT)
349 if (!fqn_prefix[whichprefix])
351 if (buffnum < 0 || buffnum >= FQN_NUMBUF) {
352 impossible("Invalid fqn_filename_buffer specified: %d", buffnum);
355 if (strlen(fqn_prefix[whichprefix]) + strlen(basenam)
356 >= FQN_MAX_FILENAME) {
357 impossible("fqname too long: %s + %s", fqn_prefix[whichprefix],
359 return basenam; /* XXX */
361 Strcpy(fqn_filename_buffer[buffnum], fqn_prefix[whichprefix]);
362 return strcat(fqn_filename_buffer[buffnum], basenam);
367 validate_prefix_locations(reasonbuf)
368 char *reasonbuf; /* reasonbuf must be at least BUFSZ, supplied by caller */
370 #if defined(NOCWD_ASSUMPTIONS)
372 const char *filename;
373 int prefcnt, failcount = 0;
374 char panicbuf1[BUFSZ], panicbuf2[BUFSZ];
380 #if defined(NOCWD_ASSUMPTIONS)
381 for (prefcnt = 1; prefcnt < PREFIX_COUNT; prefcnt++) {
382 /* don't test writing to configdir or datadir; they're readonly */
383 if (prefcnt == SYSCONFPREFIX || prefcnt == CONFIGPREFIX
384 || prefcnt == DATAPREFIX)
386 filename = fqname("validate", prefcnt, 3);
387 if ((fp = fopen(filename, "w"))) {
389 (void) unlink(filename);
393 Strcat(reasonbuf, ", ");
394 Strcat(reasonbuf, fqn_prefix_names[prefcnt]);
396 /* the paniclog entry gets the value of errno as well */
397 Sprintf(panicbuf1, "Invalid %s", fqn_prefix_names[prefcnt]);
398 #if defined(NHSTDC) && !defined(NOTSTDC)
399 if (!(details = strerror(errno)))
402 Sprintf(panicbuf2, "\"%s\", (%d) %s", fqn_prefix[prefcnt], errno,
404 paniclog(panicbuf1, panicbuf2);
415 /* fopen a file, with OS-dependent bells and whistles */
416 /* NOTE: a simpler version of this routine also exists in util/dlb_main.c */
418 fopen_datafile(filename, mode, prefix)
419 const char *filename, *mode;
424 filename = fqname(filename, prefix, prefix == TROUBLEPREFIX ? 3 : 0);
425 fp = fopen(filename, mode);
429 /* ---------- BEGIN LEVEL FILE HANDLING ----------- */
432 /* Set names for bones[] and lock[] */
437 Strcpy(levels, permbones);
438 Strcpy(bones, permbones);
440 append_slash(permbones);
441 append_slash(levels);
443 strncat(levels, bbs_id, PATHLEN);
446 Strcat(bones, "bonesnn.*");
447 Strcpy(lock, levels);
449 Strcat(lock, alllevels);
455 /* Construct a file name for a level-type file, which is of the form
456 * something.level (with any old level stripped off).
457 * This assumes there is space on the end of 'file' to append
458 * a two digit number. This is true for 'level'
459 * but be careful if you use it for other things -dgk
462 set_levelfile_name(file, lev)
468 tf = rindex(file, '.');
471 Sprintf(tf, ".%d", lev);
479 create_levelfile(lev, errbuf)
488 set_levelfile_name(lock, lev);
489 fq_lock = fqname(lock, LEVELPREFIX, 0);
491 #if defined(MICRO) || defined(WIN32)
492 /* Use O_TRUNC to force the file to be shortened if it already
493 * exists and is currently longer.
495 #ifdef HOLD_LOCKFILE_OPEN
497 fd = open_levelfile_exclusively(
498 fq_lock, lev, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
501 fd = open(fq_lock, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, FCMASK);
504 fd = maccreat(fq_lock, LEVL_TYPE);
506 fd = creat(fq_lock, FCMASK);
508 #endif /* MICRO || WIN32 */
511 level_info[lev].flags |= LFILE_EXISTS;
512 else if (errbuf) /* failure explanation */
513 Sprintf(errbuf, "Cannot create file \"%s\" for level %d (errno %d).",
520 open_levelfile(lev, errbuf)
529 set_levelfile_name(lock, lev);
530 fq_lock = fqname(lock, LEVELPREFIX, 0);
532 /* If not currently accessible, swap it in. */
533 if (level_info[lev].where != ACTIVE)
537 fd = macopen(fq_lock, O_RDONLY | O_BINARY, LEVL_TYPE);
539 #ifdef HOLD_LOCKFILE_OPEN
541 fd = open_levelfile_exclusively(fq_lock, lev, O_RDONLY | O_BINARY);
544 fd = open(fq_lock, O_RDONLY | O_BINARY, 0);
547 /* for failure, return an explanation that our caller can use;
548 settle for `lock' instead of `fq_lock' because the latter
549 might end up being too big for nethack's BUFSZ */
550 if (fd < 0 && errbuf)
552 Sprintf(errbuf, "Cannot open file \"%s\" for level %d (errno %d).",
555 Sprintf(errbuf, "
\92n
\89º%d
\8aK
\82Ì
\83t
\83@
\83C
\83\8b\"%s\"
\82ð
\8aJ
\82¯
\82È
\82¢(errno %d)
\81D",
563 delete_levelfile(lev)
567 * Level 0 might be created by port specific code that doesn't
568 * call create_levfile(), so always assume that it exists.
570 if (lev == 0 || (level_info[lev].flags & LFILE_EXISTS)) {
571 set_levelfile_name(lock, lev);
572 #ifdef HOLD_LOCKFILE_OPEN
576 (void) unlink(fqname(lock, LEVELPREFIX, 0));
577 level_info[lev].flags &= ~LFILE_EXISTS;
584 #ifdef HANGUPHANDLING
585 if (program_state.preserve_locks)
588 #if !defined(PC_LOCKING) && defined(MFLOPPY) && !defined(AMIGA)
589 eraseall(levels, alllevels);
591 eraseall(permbones, alllevels);
597 (void) signal(SIGINT, SIG_IGN);
599 #if defined(UNIX) || defined(VMS)
600 sethanguphandler((void FDECL((*), (int) )) SIG_IGN);
602 /* can't access maxledgerno() before dungeons are created -dlc */
603 for (x = (n_dgns ? maxledgerno() : 0); x >= 0; x--)
604 delete_levelfile(x); /* not all levels need be present */
606 #endif /* ?PC_LOCKING,&c */
609 #if defined(SELECTSAVED)
610 /* qsort comparison routine */
611 STATIC_OVL int CFDECLSPEC
616 #if defined(UNIX) && defined(QT_GRAPHICS)
617 return strncasecmp(*(char **) p, *(char **) q, 16);
619 return strncmpi(*(char **) p, *(char **) q, 16);
624 #ifdef HOLD_LOCKFILE_OPEN
626 open_levelfile_exclusively(name, lev, oflag)
635 if (lftrack.fd >= 0) {
636 /* check for compatible access */
637 if (lftrack.oflag == oflag) {
639 reslt = lseek(fd, 0L, SEEK_SET);
641 panic("open_levelfile_exclusively: lseek failed %d", errno);
642 lftrack.nethack_thinks_it_is_open = TRUE;
645 fd = sopen(name, oflag, SH_DENYRW, FCMASK);
647 lftrack.oflag = oflag;
648 lftrack.nethack_thinks_it_is_open = TRUE;
651 fd = sopen(name, oflag, SH_DENYRW, FCMASK);
653 lftrack.oflag = oflag;
655 lftrack.nethack_thinks_it_is_open = TRUE;
665 lftrack.nethack_thinks_it_is_open = FALSE;
677 if (lftrack.fd == fd) {
678 really_close(); /* close it, but reopen it to hold it */
679 fd = open_levelfile(0, (char *) 0);
680 lftrack.nethack_thinks_it_is_open = FALSE;
695 /* ---------- END LEVEL FILE HANDLING ----------- */
697 /* ---------- BEGIN BONES FILE HANDLING ----------- */
699 /* set up "file" to be file name for retrieving bones, and return a
700 * bonesid to be read/written in the bones file.
703 set_bonesfile_name(file, lev)
710 Sprintf(file, "bon%c%s", dungeons[lev->dnum].boneid,
711 In_quest(lev) ? urole.filecode : "0");
713 if ((sptr = Is_special(lev)) != 0)
714 Sprintf(dptr, ".%c", sptr->boneid);
716 Sprintf(dptr, ".%d", lev->dlevel);
723 /* set up temporary file name for writing bones, to avoid another game's
724 * trying to read from an uncompleted bones file. we want an uncontentious
725 * name, so use one in the namespace reserved for this game's level files.
726 * (we are not reading or writing level files while writing bones files, so
727 * the same array may be used instead of copying.)
734 tf = rindex(lock, '.');
745 create_bonesfile(lev, bonesid, errbuf)
755 *bonesid = set_bonesfile_name(bones, lev);
756 file = set_bonestemp_name();
757 file = fqname(file, BONESPREFIX, 0);
759 #if defined(MICRO) || defined(WIN32)
760 /* Use O_TRUNC to force the file to be shortened if it already
761 * exists and is currently longer.
763 fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, FCMASK);
766 fd = maccreat(file, BONE_TYPE);
768 fd = creat(file, FCMASK);
771 if (fd < 0 && errbuf) /* failure explanation */
772 Sprintf(errbuf, "Cannot create bones \"%s\", id %s (errno %d).", lock,
775 #if defined(VMS) && !defined(SECURE)
777 Re-protect bones file with world:read+write+execute+delete access.
778 umask() doesn't seem very reliable; also, vaxcrtl won't let us set
779 delete access without write access, which is what's really wanted.
780 Can't simply create it with the desired protection because creat
781 ANDs the mask with the user's default protection, which usually
782 denies some or all access to world.
784 (void) chmod(file, FCMASK | 007); /* allow other users full access */
785 #endif /* VMS && !SECURE */
791 /* remove partial bonesfile in process of creation */
795 const char *tempname;
797 tempname = set_bonestemp_name();
798 tempname = fqname(tempname, BONESPREFIX, 0);
799 (void) unlink(tempname);
803 /* move completed bones file to proper name */
805 commit_bonesfile(lev)
808 const char *fq_bones, *tempname;
811 (void) set_bonesfile_name(bones, lev);
812 fq_bones = fqname(bones, BONESPREFIX, 0);
813 tempname = set_bonestemp_name();
814 tempname = fqname(tempname, BONESPREFIX, 1);
816 #if (defined(SYSV) && !defined(SVR4)) || defined(GENIX)
817 /* old SYSVs don't have rename. Some SVR3's may, but since they
818 * also have link/unlink, it doesn't matter. :-)
820 (void) unlink(fq_bones);
821 ret = link(tempname, fq_bones);
822 ret += unlink(tempname);
824 ret = rename(tempname, fq_bones);
826 if (wizard && ret != 0)
827 pline("couldn't rename %s to %s.", tempname, fq_bones);
831 open_bonesfile(lev, bonesid)
835 const char *fq_bones;
838 *bonesid = set_bonesfile_name(bones, lev);
839 fq_bones = fqname(bones, BONESPREFIX, 0);
840 nh_uncompress(fq_bones); /* no effect if nonexistent */
842 fd = macopen(fq_bones, O_RDONLY | O_BINARY, BONE_TYPE);
844 fd = open(fq_bones, O_RDONLY | O_BINARY, 0);
850 delete_bonesfile(lev)
853 (void) set_bonesfile_name(bones, lev);
854 return !(unlink(fqname(bones, BONESPREFIX, 0)) < 0);
857 /* assume we're compressing the recently read or created bonesfile, so the
858 * file name is already set properly */
862 nh_compress(fqname(bones, BONESPREFIX, 0));
865 /* ---------- END BONES FILE HANDLING ----------- */
867 /* ---------- BEGIN SAVE FILE HANDLING ----------- */
869 /* set savefile name in OS-dependent manner from pre-existing plname,
870 * avoiding troublesome characters */
872 set_savefile_name(regularize_it)
873 boolean regularize_it;
876 Sprintf(SAVEF, "[.save]%d%s", getuid(), plname);
878 regularize(SAVEF + 7);
882 Strcpy(SAVEF, SAVEP);
884 strncat(SAVEF, bbs_id, PATHLEN);
887 int i = strlen(SAVEP);
889 /* plname has to share space with SAVEP and ".sav" */
890 (void) strncat(SAVEF, plname, FILENAME - i - 4);
892 (void) strncat(SAVEF, plname, 8);
895 regularize(SAVEF + i);
897 Strcat(SAVEF, SAVE_EXTENSION);
901 static const char okchars[] =
902 "*ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.";
903 const char *legal = okchars;
904 char fnamebuf[BUFSZ], encodedfnamebuf[BUFSZ];
906 /* Obtain the name of the logged on user and incorporate
907 * it into the name. */
908 Sprintf(fnamebuf, "%s-%s", get_username(0), plname);
910 ++legal; /* skip '*' wildcard character */
911 (void) fname_encode(legal, '%', fnamebuf, encodedfnamebuf, BUFSZ);
912 Sprintf(SAVEF, "%s%s", encodedfnamebuf, SAVE_EXTENSION);
914 #else /* not VMS or MICRO or WIN32 */
915 Sprintf(SAVEF, "save/%d%s", (int) getuid(), plname);
917 regularize(SAVEF + 5); /* avoid . or / in name */
925 save_savefile_name(fd)
928 (void) write(fd, (genericptr_t) SAVEF, sizeof(SAVEF));
933 /* change pre-existing savefile name to indicate an error savefile */
939 char *semi_colon = rindex(SAVEF, ';');
944 Strcat(SAVEF, ".e;1");
955 /* create save file, overwriting one if it already exists */
962 fq_save = fqname(SAVEF, SAVEPREFIX, 0);
963 #if defined(MICRO) || defined(WIN32)
964 fd = open(fq_save, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FCMASK);
967 fd = maccreat(fq_save, SAVE_TYPE);
969 fd = creat(fq_save, FCMASK);
971 #if defined(VMS) && !defined(SECURE)
973 Make sure the save file is owned by the current process. That's
974 the default for non-privileged users, but for priv'd users the
975 file will be owned by the directory's owner instead of the user.
978 (void) chown(fq_save, getuid(), getgid());
979 #define getuid() vms_getuid()
980 #endif /* VMS && !SECURE */
986 /* open savefile for reading */
993 fq_save = fqname(SAVEF, SAVEPREFIX, 0);
995 fd = macopen(fq_save, O_RDONLY | O_BINARY, SAVE_TYPE);
997 fd = open(fq_save, O_RDONLY | O_BINARY, 0);
1002 /* delete savefile */
1006 (void) unlink(fqname(SAVEF, SAVEPREFIX, 0));
1007 return 0; /* for restore_saved_game() (ex-xxxmain.c) test */
1010 /* try to open up a save file and prepare to restore it */
1012 restore_saved_game()
1014 const char *fq_save;
1018 set_savefile_name(TRUE);
1020 if (!saveDiskPrompt(1))
1022 #endif /* MFLOPPY */
1023 fq_save = fqname(SAVEF, SAVEPREFIX, 0);
1025 nh_uncompress(fq_save);
1026 if ((fd = open_savefile()) < 0)
1029 if (validate(fd, fq_save) != 0) {
1030 (void) nhclose(fd), fd = -1;
1031 (void) delete_savefile();
1036 #if defined(SELECTSAVED)
1038 plname_from_file(filename)
1039 const char *filename;
1044 Strcpy(SAVEF, filename);
1045 #ifdef COMPRESS_EXTENSION
1046 SAVEF[strlen(SAVEF) - strlen(COMPRESS_EXTENSION)] = '\0';
1048 nh_uncompress(SAVEF);
1049 if ((fd = open_savefile()) >= 0) {
1050 if (validate(fd, filename) == 0) {
1051 char tplname[PL_NSIZ];
1052 get_plname_from_file(fd, tplname);
1053 result = dupstr(tplname);
1061 /* --------- obsolete - used to be ifndef STORE_PLNAME_IN_FILE ----*/
1062 #if defined(UNIX) && defined(QT_GRAPHICS)
1063 /* Name not stored in save file, so we have to extract it from
1064 the filename, which loses information
1065 (eg. "/", "_", and "." characters are lost. */
1068 char name[64]; /* more than PL_NSIZ */
1069 #ifdef COMPRESS_EXTENSION
1070 #define EXTSTR COMPRESS_EXTENSION
1075 if ( sscanf( filename, "%*[^/]/%d%63[^.]" EXTSTR, &uid, name ) == 2 ) {
1077 /* "_" most likely means " ", which certainly looks nicer */
1078 for (k=0; name[k]; k++)
1079 if ( name[k] == '_' )
1081 return dupstr(name);
1083 #endif /* UNIX && QT_GRAPHICS */
1087 /* --------- end of obsolete code ----*/
1088 #endif /* 0 - WAS STORE_PLNAME_IN_FILE*/
1090 #endif /* defined(SELECTSAVED) */
1095 #if defined(SELECTSAVED)
1101 const char *fq_save;
1103 Strcpy(plname, "*");
1104 set_savefile_name(FALSE);
1105 #if defined(ZLIB_COMP)
1106 Strcat(SAVEF, COMPRESS_EXTENSION);
1108 fq_save = fqname(SAVEF, SAVEPREFIX, 0);
1111 foundfile = foundfile_buffer();
1112 if (findfirst((char *) fq_save)) {
1115 } while (findnext());
1118 result = (char **) alloc((n + 1) * sizeof(char *)); /* at most */
1119 (void) memset((genericptr_t) result, 0, (n + 1) * sizeof(char *));
1120 if (findfirst((char *) fq_save)) {
1124 r = plname_from_file(foundfile);
1128 } while (findnext());
1133 #if defined(UNIX) && defined(QT_GRAPHICS)
1134 /* posixly correct version */
1135 int myuid = getuid();
1138 if ((dir = opendir(fqname("save", SAVEPREFIX, 0)))) {
1139 for (n = 0; readdir(dir); n++)
1145 if (!(dir = opendir(fqname("save", SAVEPREFIX, 0))))
1147 result = (char **) alloc((n + 1) * sizeof(char *)); /* at most */
1148 (void) memset((genericptr_t) result, 0, (n + 1) * sizeof(char *));
1149 for (i = 0, j = 0; i < n; i++) {
1151 char name[64]; /* more than PL_NSIZ */
1152 struct dirent *entry = readdir(dir);
1156 if (sscanf(entry->d_name, "%d%63s", &uid, name) == 2) {
1158 char filename[BUFSZ];
1161 Sprintf(filename, "save/%d%s", uid, name);
1162 r = plname_from_file(filename);
1173 Strcpy(plname, "*");
1174 set_savefile_name(FALSE);
1175 j = vms_get_saved_games(SAVEF, &result);
1180 qsort(result, j, sizeof (char *), strcmp_wrap);
1183 } else if (result) { /* could happen if save files are obsolete */
1184 free_saved_games(result);
1186 #endif /* SELECTSAVED */
1191 free_saved_games(saved)
1198 free((genericptr_t) saved[i++]);
1199 free((genericptr_t) saved);
1203 /* ---------- END SAVE FILE HANDLING ----------- */
1205 /* ---------- BEGIN FILE COMPRESSION HANDLING ----------- */
1210 redirect(filename, mode, stream, uncomp)
1211 const char *filename, *mode;
1215 if (freopen(filename, mode, stream) == (FILE *) 0) {
1216 (void) fprintf(stderr, "freopen of %s for %scompress failed\n",
1217 filename, uncomp ? "un" : "");
1218 terminate(EXIT_FAILURE);
1223 * using system() is simpler, but opens up security holes and causes
1224 * problems on at least Interactive UNIX 3.0.1 (SVR3.2), where any
1225 * setuid is renounced by /bin/sh, so the files cannot be accessed.
1227 * cf. child() in unixunix.c.
1230 docompress_file(filename, uncomp)
1231 const char *filename;
1236 const char *args[10];
1237 #ifdef COMPRESS_OPTIONS
1243 boolean istty = !strncmpi(windowprocs.name, "tty", 3);
1246 Strcpy(cfn, filename);
1247 #ifdef COMPRESS_EXTENSION
1248 Strcat(cfn, COMPRESS_EXTENSION);
1250 /* when compressing, we know the file exists */
1252 if ((cf = fopen(cfn, RDBMODE)) == (FILE *) 0)
1259 args[++i] = "-d"; /* uncompress */
1260 #ifdef COMPRESS_OPTIONS
1262 /* we can't guarantee there's only one additional option, sigh */
1264 boolean inword = FALSE;
1266 Strcpy(opts, COMPRESS_OPTIONS);
1269 if ((*opt == ' ') || (*opt == '\t')) {
1274 } else if (!inword) {
1282 args[++i] = (char *) 0;
1285 /* If we don't do this and we are right after a y/n question *and*
1286 * there is an error message from the compression, the 'y' or 'n' can
1287 * end up being displayed after the error message.
1293 if (f == 0) { /* child */
1295 /* any error messages from the compression must come out after
1296 * the first line, because the more() to let the user read
1297 * them will have to clear the first line. This should be
1298 * invisible if there are no error messages.
1303 /* run compressor without privileges, in case other programs
1304 * have surprises along the line of gzip once taking filenames
1307 /* assume all compressors will compress stdin to stdout
1308 * without explicit filenames. this is true of at least
1309 * compress and gzip, those mentioned in config.h.
1312 redirect(cfn, RDBMODE, stdin, uncomp);
1313 redirect(filename, WRBMODE, stdout, uncomp);
1315 redirect(filename, RDBMODE, stdin, uncomp);
1316 redirect(cfn, WRBMODE, stdout, uncomp);
1318 (void) setgid(getgid());
1319 (void) setuid(getuid());
1320 (void) execv(args[0], (char *const *) args);
1322 (void) fprintf(stderr, "Exec to %scompress %s failed.\n",
1323 uncomp ? "un" : "", filename);
1324 terminate(EXIT_FAILURE);
1325 } else if (f == -1) {
1327 pline("Fork to %scompress %s failed.", uncomp ? "un" : "", filename);
1331 (void) signal(SIGINT, SIG_IGN);
1332 (void) signal(SIGQUIT, SIG_IGN);
1333 (void) wait((int *) &i);
1334 (void) signal(SIGINT, (SIG_RET_TYPE) done1);
1336 (void) signal(SIGQUIT, SIG_DFL);
1338 /* I don't think we can really cope with external compression
1339 * without signals, so we'll declare that compress failed and
1340 * go on. (We could do a better job by forcing off external
1341 * compression if there are no signals, but we want this for
1342 * testing with FailSafeC
1347 /* (un)compress succeeded: remove file left behind */
1351 (void) unlink(filename);
1353 /* (un)compress failed; remove the new, bad file */
1355 raw_printf("Unable to uncompress %s", filename);
1356 (void) unlink(filename);
1358 /* no message needed for compress case; life will go on */
1362 /* Give them a chance to read any error messages from the
1363 * compression--these would go to stdout or stderr and would get
1364 * overwritten only in tty mode. It's still ugly, since the
1365 * messages are being written on top of the screen, but at least
1366 * the user can read them.
1368 if (istty && iflags.window_inited) {
1369 clear_nhwindow(WIN_MESSAGE);
1371 /* No way to know if this is feasible */
1377 #endif /* COMPRESS */
1379 #if defined(COMPRESS) || defined(ZLIB_COMP)
1380 #define UNUSED_if_not_COMPRESS /*empty*/
1382 #define UNUSED_if_not_COMPRESS UNUSED
1387 nh_compress(filename)
1388 const char *filename UNUSED_if_not_COMPRESS;
1390 #if !defined(COMPRESS) && !defined(ZLIB_COMP)
1391 #ifdef PRAGMA_UNUSED
1392 #pragma unused(filename)
1395 docompress_file(filename, FALSE);
1399 /* uncompress file if it exists */
1401 nh_uncompress(filename)
1402 const char *filename UNUSED_if_not_COMPRESS;
1404 #if !defined(COMPRESS) && !defined(ZLIB_COMP)
1405 #ifdef PRAGMA_UNUSED
1406 #pragma unused(filename)
1409 docompress_file(filename, TRUE);
1413 #ifdef ZLIB_COMP /* RLC 09 Mar 1999: Support internal ZLIB */
1415 make_compressed_name(filename, cfn)
1416 const char *filename;
1419 #ifndef SHORT_FILENAMES
1420 /* Assume free-form filename with no 8.3 restrictions */
1421 strcpy(cfn, filename);
1422 strcat(cfn, COMPRESS_EXTENSION);
1425 #ifdef SAVE_EXTENSION
1426 char *bp = (char *) 0;
1428 strcpy(cfn, filename);
1429 if ((bp = strstri(cfn, SAVE_EXTENSION))) {
1430 strsubst(bp, SAVE_EXTENSION, ".saz");
1433 /* find last occurrence of bon */
1435 while (bp-- > cfn) {
1436 if (strstri(bp, "bon")) {
1437 strsubst(bp, "bon", "boz");
1442 #endif /* SAVE_EXTENSION */
1444 #endif /* SHORT_FILENAMES */
1448 docompress_file(filename, uncomp)
1449 const char *filename;
1452 gzFile compressedfile;
1453 FILE *uncompressedfile;
1458 if (!make_compressed_name(filename, cfn))
1462 /* Open the input and output files */
1463 /* Note that gzopen takes "wb" as its mode, even on systems where
1464 fopen takes "r" and "w" */
1466 uncompressedfile = fopen(filename, RDBMODE);
1467 if (!uncompressedfile) {
1468 pline("Error in zlib docompress_file %s", filename);
1471 compressedfile = gzopen(cfn, "wb");
1472 if (compressedfile == NULL) {
1474 pline("zlib failed to allocate memory");
1476 panic("Error in docompress_file %d", errno);
1478 fclose(uncompressedfile);
1482 /* Copy from the uncompressed to the compressed file */
1485 len = fread(buf, 1, sizeof(buf), uncompressedfile);
1486 if (ferror(uncompressedfile)) {
1487 pline("Failure reading uncompressed file");
1488 pline("Can't compress %s.", filename);
1489 fclose(uncompressedfile);
1490 gzclose(compressedfile);
1495 break; /* End of file */
1497 len2 = gzwrite(compressedfile, buf, len);
1499 pline("Failure writing compressed file");
1500 pline("Can't compress %s.", filename);
1501 fclose(uncompressedfile);
1502 gzclose(compressedfile);
1508 fclose(uncompressedfile);
1509 gzclose(compressedfile);
1511 /* Delete the file left behind */
1513 (void) unlink(filename);
1515 } else { /* uncomp */
1517 /* Open the input and output files */
1518 /* Note that gzopen takes "rb" as its mode, even on systems where
1519 fopen takes "r" and "w" */
1521 compressedfile = gzopen(cfn, "rb");
1522 if (compressedfile == NULL) {
1524 pline("zlib failed to allocate memory");
1525 } else if (errno != ENOENT) {
1526 panic("Error in zlib docompress_file %s, %d", filename,
1531 uncompressedfile = fopen(filename, WRBMODE);
1532 if (!uncompressedfile) {
1533 pline("Error in zlib docompress file uncompress %s", filename);
1534 gzclose(compressedfile);
1538 /* Copy from the compressed to the uncompressed file */
1541 len = gzread(compressedfile, buf, sizeof(buf));
1542 if (len == (unsigned) -1) {
1543 pline("Failure reading compressed file");
1544 pline("Can't uncompress %s.", filename);
1545 fclose(uncompressedfile);
1546 gzclose(compressedfile);
1547 (void) unlink(filename);
1551 break; /* End of file */
1553 fwrite(buf, 1, len, uncompressedfile);
1554 if (ferror(uncompressedfile)) {
1555 pline("Failure writing uncompressed file");
1556 pline("Can't uncompress %s.", filename);
1557 fclose(uncompressedfile);
1558 gzclose(compressedfile);
1559 (void) unlink(filename);
1564 fclose(uncompressedfile);
1565 gzclose(compressedfile);
1567 /* Delete the file left behind */
1571 #endif /* RLC 09 Mar 1999: End ZLIB patch */
1573 /* ---------- END FILE COMPRESSION HANDLING ----------- */
1575 /* ---------- BEGIN FILE LOCKING HANDLING ----------- */
1577 static int nesting = 0;
1579 #if defined(NO_FILE_LINKS) || defined(USE_FCNTL) /* implies UNIX */
1580 static int lockfd; /* for lock_file() to pass to unlock_file() */
1583 struct flock sflock; /* for unlocking, same as above */
1586 #define HUP if (!program_state.done_hup)
1590 make_lockname(filename, lockname)
1591 const char *filename;
1594 #if defined(UNIX) || defined(VMS) || defined(AMIGA) || defined(WIN32) \
1596 #ifdef NO_FILE_LINKS
1597 Strcpy(lockname, LOCKDIR);
1598 Strcat(lockname, "/");
1599 Strcat(lockname, filename);
1601 Strcpy(lockname, filename);
1605 char *semi_colon = rindex(lockname, ';');
1609 Strcat(lockname, ".lock;1");
1611 Strcat(lockname, "_lock");
1614 #else /* !(UNIX || VMS || AMIGA || WIN32 || MSDOS) */
1615 #ifdef PRAGMA_UNUSED
1616 #pragma unused(filename)
1622 #endif /* !USE_FCNTL */
1626 lock_file(filename, whichprefix, retryct)
1627 const char *filename;
1631 #if defined(PRAGMA_UNUSED) && !(defined(UNIX) || defined(VMS)) \
1632 && !(defined(AMIGA) || defined(WIN32) || defined(MSDOS))
1633 #pragma unused(retryct)
1636 char locknambuf[BUFSZ];
1637 const char *lockname;
1642 impossible("TRIED TO NEST LOCKS");
1647 lockname = make_lockname(filename, locknambuf);
1648 #ifndef NO_FILE_LINKS /* LOCKDIR should be subsumed by LOCKPREFIX */
1649 lockname = fqname(lockname, LOCKPREFIX, 2);
1652 filename = fqname(filename, whichprefix, 0);
1654 lockfd = open(filename, O_RDWR);
1656 HUP raw_printf("Cannot open file %s. This is a program bug.",
1659 sflock.l_type = F_WRLCK;
1660 sflock.l_whence = SEEK_SET;
1665 #if defined(UNIX) || defined(VMS)
1667 while (fcntl(lockfd, F_SETLK, &sflock) == -1) {
1669 #ifdef NO_FILE_LINKS
1670 while ((lockfd = open(lockname, O_RDWR | O_CREAT | O_EXCL, 0666)) == -1) {
1672 while (link(filename, lockname) == -1) {
1679 "Waiting for release of fcntl lock on %s. (%d retries left).",
1683 HUP(void) raw_print("I give up. Sorry.");
1684 HUP raw_printf("Some other process has an unnatural grip on %s.",
1690 register int errnosv = errno;
1692 switch (errnosv) { /* George Barbanis */
1696 "Waiting for access to %s. (%d retries left).", filename,
1698 #if defined(SYSV) || defined(ULTRIX) || defined(VMS)
1703 HUP(void) raw_print("I give up. Sorry.");
1704 HUP raw_printf("Perhaps there is an old %s around?",
1712 HUP raw_printf("Can't find file %s to lock!", filename);
1716 HUP raw_printf("No write permission to lock %s!", filename);
1719 #ifdef VMS /* c__translate(vmsfiles.c) */
1721 /* could be misleading, but usually right */
1722 HUP raw_printf("Can't lock %s due to directory protection.",
1728 /* take a wild guess at the underlying cause */
1729 HUP perror(lockname);
1730 HUP raw_printf("Cannot lock %s.", filename);
1732 "(Perhaps you are running NetHack from inside the distribution package?).");
1736 HUP perror(lockname);
1737 HUP raw_printf("Cannot lock %s for unknown reason (%d).",
1742 #endif /* USE_FCNTL */
1744 #endif /* UNIX || VMS */
1746 #if (defined(AMIGA) || defined(WIN32) || defined(MSDOS)) \
1747 && !defined(USE_FCNTL)
1749 #define OPENFAILURE(fd) (!fd)
1752 #define OPENFAILURE(fd) (fd < 0)
1755 while (--retryct && OPENFAILURE(lockptr)) {
1756 #if defined(WIN32) && !defined(WIN_CE)
1757 lockptr = sopen(lockname, O_RDWR | O_CREAT, SH_DENYRW, S_IWRITE);
1759 (void) DeleteFile(lockname); /* in case dead process was here first */
1761 lockptr = Open(lockname, MODE_NEWFILE);
1763 lockptr = open(lockname, O_RDWR | O_CREAT | O_EXCL, S_IWRITE);
1766 if (OPENFAILURE(lockptr)) {
1767 raw_printf("Waiting for access to %s. (%d retries left).",
1773 raw_printf("I give up. Sorry.");
1777 #endif /* AMIGA || WIN32 || MSDOS */
1781 #ifdef VMS /* for unlock_file, use the unlink() routine in vmsunix.c */
1785 #define unlink(foo) vms_unlink(foo)
1788 /* unlock file, which must be currently locked by lock_file */
1790 unlock_file(filename)
1791 const char *filename;
1794 char locknambuf[BUFSZ];
1795 const char *lockname;
1800 sflock.l_type = F_UNLCK;
1801 if (fcntl(lockfd, F_SETLK, &sflock) == -1) {
1802 HUP raw_printf("Can't remove fcntl lock on %s.", filename);
1803 (void) close(lockfd);
1806 lockname = make_lockname(filename, locknambuf);
1807 #ifndef NO_FILE_LINKS /* LOCKDIR should be subsumed by LOCKPREFIX */
1808 lockname = fqname(lockname, LOCKPREFIX, 2);
1811 #if defined(UNIX) || defined(VMS)
1812 if (unlink(lockname) < 0)
1813 HUP raw_printf("Can't unlink %s.", lockname);
1814 #ifdef NO_FILE_LINKS
1815 (void) nhclose(lockfd);
1818 #endif /* UNIX || VMS */
1820 #if defined(AMIGA) || defined(WIN32) || defined(MSDOS)
1823 DeleteFile(lockname);
1825 #endif /* AMIGA || WIN32 || MSDOS */
1826 #endif /* USE_FCNTL */
1832 /* ---------- END FILE LOCKING HANDLING ----------- */
1834 /* ---------- BEGIN CONFIG FILE HANDLING ----------- */
1836 const char *configfile =
1840 #if defined(MAC) || defined(__BEOS__)
1843 #if defined(MSDOS) || defined(WIN32)
1851 /* used for messaging */
1852 char lastconfigfile[BUFSZ];
1855 /* conflict with speed-dial under windows
1856 * for XXX.cnf file so support of NetHack.cnf
1857 * is for backward compatibility only.
1858 * Preferred name (and first tried) is now defaults.nh but
1859 * the game will try the old name if there
1860 * is no defaults.nh.
1862 const char *backward_compat_configfile = "nethack.cnf";
1866 #define fopenp fopen
1870 fopen_config_file(filename, src)
1871 const char *filename;
1875 #if defined(UNIX) || defined(VMS)
1876 char tmp_config[BUFSZ];
1880 /* If src != SET_IN_SYS, "filename" is an environment variable, so it
1881 * should hang around. If set, it is expected to be a full path name
1885 if ((src != SET_IN_SYS) && access(filename, 4) == -1) {
1886 /* 4 is R_OK on newer systems */
1887 /* nasty sneaky attempt to read file through
1888 * NetHack's setuid permissions -- this is the only
1889 * place a file name may be wholly under the player's
1890 * control (but SYSCF_FILE is not under the player's
1891 * control so it's OK).
1893 raw_printf("Access to %s denied (%d).", filename, errno);
1895 /* fall through to standard names */
1898 #ifdef PREFIXES_IN_USE
1899 if (src == SET_IN_SYS) {
1900 (void) strncpy(lastconfigfile, fqname(filename, SYSCONFPREFIX, 0),
1904 /* always honor sysconf first before anything else */
1905 (void) strncpy(lastconfigfile, filename, BUFSZ - 1);
1906 lastconfigfile[BUFSZ - 1] = '\0';
1907 if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0)
1909 if ((fp = fopenp(filename, "r")) != (FILE *) 0) {
1911 #if defined(UNIX) || defined(VMS)
1913 /* access() above probably caught most problems for UNIX */
1914 raw_printf("Couldn't open requested config file %s (%d).",
1917 /* fall through to standard names */
1922 #if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32)
1923 if ((fp = fopenp(fqname(configfile, CONFIGPREFIX, 0), "r")) != (FILE *) 0)
1925 if ((fp = fopenp(configfile, "r")) != (FILE *) 0)
1928 if ((fp = fopenp(fqname(backward_compat_configfile, CONFIGPREFIX, 0),
1929 "r")) != (FILE *) 0)
1931 if ((fp = fopenp(backward_compat_configfile, "r")) != (FILE *) 0)
1935 /* constructed full path names don't need fqname() */
1937 (void) strncpy(lastconfigfile, fqname("nethackini", CONFIGPREFIX, 0),
1939 lastconfigfile[BUFSZ - 1] = '\0';
1940 if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0) {
1943 (void) strncpy(lastconfigfile, "sys$login:nethack.ini", BUFSZ - 1);
1944 lastconfigfile[BUFSZ - 1] = '\0';
1945 if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0) {
1949 envp = nh_getenv("HOME");
1951 Strcpy(tmp_config, "NetHack.cnf");
1953 Sprintf(tmp_config, "%s%s", envp, "NetHack.cnf");
1955 (void) strncpy(lastconfigfile, tmp_config, BUFSZ - 1);
1956 lastconfigfile[BUFSZ - 1] = '\0';
1957 if ((fp = fopenp(tmp_config, "r")) != (FILE *) 0)
1959 #else /* should be only UNIX left */
1960 envp = nh_getenv("HOME");
1961 #if 1 /*JP*//*".jnethackrc"
\82ð
\97D
\90æ
\82µ
\82Ä
\93Ç
\82Ý
\8d\9e\82Ý*/
1963 Strcpy(tmp_config, ".jnethackrc");
1965 Sprintf(tmp_config, "%s/%s", envp, ".jnethackrc");
1967 (void) strncpy(lastconfigfile, tmp_config, BUFSZ - 1);
1968 if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0)
1972 Strcpy(tmp_config, ".nethackrc");
1974 Sprintf(tmp_config, "%s/%s", envp, ".nethackrc");
1976 (void) strncpy(lastconfigfile, tmp_config, BUFSZ - 1);
1977 lastconfigfile[BUFSZ - 1] = '\0';
1978 if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0)
1980 #if defined(__APPLE__)
1981 /* try an alternative */
1983 Sprintf(tmp_config, "%s/%s", envp,
1984 "Library/Preferences/NetHack Defaults");
1985 (void) strncpy(lastconfigfile, tmp_config, BUFSZ - 1);
1986 lastconfigfile[BUFSZ - 1] = '\0';
1987 if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0)
1989 Sprintf(tmp_config, "%s/%s", envp,
1990 "Library/Preferences/NetHack Defaults.txt");
1991 (void) strncpy(lastconfigfile, tmp_config, BUFSZ - 1);
1992 lastconfigfile[BUFSZ - 1] = '\0';
1993 if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0)
1997 if (errno != ENOENT) {
1998 const char *details;
2000 /* e.g., problems when setuid NetHack can't search home
2001 * directory restricted to user */
2003 #if defined(NHSTDC) && !defined(NOTSTDC)
2004 if ((details = strerror(errno)) == 0)
2007 raw_printf("Couldn't open default config file %s %s(%d).",
2008 lastconfigfile, details, errno);
2017 * Retrieve a list of integers from a file into a uchar array.
2019 * NOTE: zeros are inserted unless modlist is TRUE, in which case the list
2020 * location is unchanged. Callers must handle zeros if modlist is FALSE.
2023 get_uchars(fp, buf, bufp, list, modlist, size, name)
2024 FILE *fp; /* input file pointer */
2025 char *buf; /* read buffer, must be of size BUFSZ */
2026 char *bufp; /* current pointer */
2027 uchar *list; /* return list */
2028 boolean modlist; /* TRUE: list is being modified in place */
2029 int size; /* return list size */
2030 const char *name; /* name of option for error message */
2032 unsigned int num = 0;
2034 boolean havenum = FALSE;
2043 /* if modifying in place, don't insert zeros */
2044 if (num || !modlist)
2050 if (count == size || !*bufp)
2066 num = num * 10 + (*bufp - '0');
2071 if (fp == (FILE *) 0)
2074 if (!fgets(buf, BUFSZ, fp))
2076 } while (buf[0] == '#');
2082 raw_printf("Syntax error in %s", name);
2090 #ifdef NOCWD_ASSUMPTIONS
2092 adjust_prefix(bufp, prefixid)
2100 /* Backward compatibility, ignore trailing ;n */
2101 if ((ptr = index(bufp, ';')) != 0)
2103 if (strlen(bufp) > 0) {
2104 fqn_prefix[prefixid] = (char *) alloc(strlen(bufp) + 2);
2105 Strcpy(fqn_prefix[prefixid], bufp);
2106 append_slash(fqn_prefix[prefixid]);
2111 #define match_varname(INP, NAM, LEN) match_optname(INP, NAM, LEN, TRUE)
2114 parse_config_line(fp, origbuf, src)
2119 #if defined(MICRO) && !defined(NOCWD_ASSUMPTIONS)
2120 static boolean ramdisk_specified = FALSE;
2125 char *bufp, *altp, buf[BUFSZ];
2126 uchar translate[MAXPCHARS];
2129 /* convert any tab to space, condense consecutive spaces into one,
2130 remove leading and trailing spaces (exception: if there is nothing
2131 but spaces, one of them will be kept even though it leads/trails) */
2132 mungspaces(strcpy(buf, origbuf));
2133 /* lines beginning with '#' are comments; accept empty lines too */
2134 if (!*buf || *buf == '#' || !strcmp(buf, " "))
2137 /* find the '=' or ':' */
2138 bufp = index(buf, '=');
2139 altp = index(buf, ':');
2140 if (!bufp || (altp && altp < bufp))
2144 /* skip past '=', then space between it and value, if any */
2149 /* Go through possible variables */
2150 /* some of these (at least LEVELS and SAVE) should now set the
2151 * appropriate fqn_prefix[] rather than specialized variables
2153 if (match_varname(buf, "OPTIONS", 4)) {
2154 /* hack: un-mungspaces to allow consecutive spaces in
2155 general options until we verify that this is unnecessary;
2156 '=' or ':' is guaranteed to be present */
2157 bufp = index(origbuf, '=');
2158 altp = index(origbuf, ':');
2159 if (!bufp || (altp && altp < bufp))
2161 ++bufp; /* skip '='; parseoptions() handles spaces */
2163 parseoptions(bufp, TRUE, TRUE);
2164 if (plname[0]) /* If a name was given */
2165 plnamesuffix(); /* set the character class */
2166 } else if (match_varname(buf, "AUTOPICKUP_EXCEPTION", 5)) {
2167 add_autopickup_exception(bufp);
2168 } else if (match_varname(buf, "MSGTYPE", 7)) {
2169 (void) msgtype_parse_add(bufp);
2170 #ifdef NOCWD_ASSUMPTIONS
2171 } else if (match_varname(buf, "HACKDIR", 4)) {
2172 adjust_prefix(bufp, HACKPREFIX);
2173 } else if (match_varname(buf, "LEVELDIR", 4)
2174 || match_varname(buf, "LEVELS", 4)) {
2175 adjust_prefix(bufp, LEVELPREFIX);
2176 } else if (match_varname(buf, "SAVEDIR", 4)) {
2177 adjust_prefix(bufp, SAVEPREFIX);
2178 } else if (match_varname(buf, "BONESDIR", 5)) {
2179 adjust_prefix(bufp, BONESPREFIX);
2180 } else if (match_varname(buf, "DATADIR", 4)) {
2181 adjust_prefix(bufp, DATAPREFIX);
2182 } else if (match_varname(buf, "SCOREDIR", 4)) {
2183 adjust_prefix(bufp, SCOREPREFIX);
2184 } else if (match_varname(buf, "LOCKDIR", 4)) {
2185 adjust_prefix(bufp, LOCKPREFIX);
2186 } else if (match_varname(buf, "CONFIGDIR", 4)) {
2187 adjust_prefix(bufp, CONFIGPREFIX);
2188 } else if (match_varname(buf, "TROUBLEDIR", 4)) {
2189 adjust_prefix(bufp, TROUBLEPREFIX);
2190 #else /*NOCWD_ASSUMPTIONS*/
2192 } else if (match_varname(buf, "HACKDIR", 4)) {
2193 (void) strncpy(hackdir, bufp, PATHLEN - 1);
2195 } else if (match_varname(buf, "RAMDISK", 3)) {
2196 /* The following ifdef is NOT in the wrong
2197 * place. For now, we accept and silently
2200 if (strlen(bufp) >= PATHLEN)
2201 bufp[PATHLEN - 1] = '\0';
2202 Strcpy(levels, bufp);
2203 ramdisk = (strcmp(permbones, levels) != 0);
2204 ramdisk_specified = TRUE;
2207 } else if (match_varname(buf, "LEVELS", 4)) {
2208 if (strlen(bufp) >= PATHLEN)
2209 bufp[PATHLEN - 1] = '\0';
2210 Strcpy(permbones, bufp);
2211 if (!ramdisk_specified || !*levels)
2212 Strcpy(levels, bufp);
2213 ramdisk = (strcmp(permbones, levels) != 0);
2214 } else if (match_varname(buf, "SAVE", 4)) {
2216 extern int saveprompt;
2220 if ((ptr = index(bufp, ';')) != 0) {
2223 if (*(ptr + 1) == 'n' || *(ptr + 1) == 'N') {
2228 #if defined(SYSFLAGS) && defined(MFLOPPY)
2230 saveprompt = sysflags.asksavedisk;
2233 (void) strncpy(SAVEP, bufp, SAVESIZE - 1);
2234 append_slash(SAVEP);
2236 #endif /*NOCWD_ASSUMPTIONS*/
2238 } else if (match_varname(buf, "NAME", 4)) {
2239 (void) strncpy(plname, bufp, PL_NSIZ - 1);
2241 } else if (match_varname(buf, "ROLE", 4)
2242 || match_varname(buf, "CHARACTER", 4)) {
2243 if ((len = str2role(bufp)) >= 0)
2244 flags.initrole = len;
2245 } else if (match_varname(buf, "DOGNAME", 3)) {
2246 (void) strncpy(dogname, bufp, PL_PSIZ - 1);
2247 } else if (match_varname(buf, "CATNAME", 3)) {
2248 (void) strncpy(catname, bufp, PL_PSIZ - 1);
2251 } else if (src == SET_IN_SYS && match_varname(buf, "WIZARDS", 7)) {
2253 free((genericptr_t) sysopt.wizards);
2254 sysopt.wizards = dupstr(bufp);
2255 if (strlen(sysopt.wizards) && strcmp(sysopt.wizards, "*")) {
2256 /* pre-format WIZARDS list now; it's displayed during a panic
2257 and since that panic might be due to running out of memory,
2258 we don't want to risk attempting to allocate any memory then */
2259 if (sysopt.fmtd_wizard_list)
2260 free((genericptr_t) sysopt.fmtd_wizard_list);
2261 sysopt.fmtd_wizard_list = build_english_list(sysopt.wizards);
2263 } else if (src == SET_IN_SYS && match_varname(buf, "SHELLERS", 8)) {
2264 if (sysopt.shellers)
2265 free((genericptr_t) sysopt.shellers);
2266 sysopt.shellers = dupstr(bufp);
2267 } else if (src == SET_IN_SYS && match_varname(buf, "EXPLORERS", 7)) {
2268 if (sysopt.explorers)
2269 free((genericptr_t) sysopt.explorers);
2270 sysopt.explorers = dupstr(bufp);
2271 } else if (src == SET_IN_SYS && match_varname(buf, "DEBUGFILES", 5)) {
2272 /* if showdebug() has already been called (perhaps we've added
2273 some debugpline() calls to option processing) and has found
2274 a value for getenv("DEBUGFILES"), don't override that */
2275 if (sysopt.env_dbgfl <= 0) {
2276 if (sysopt.debugfiles)
2277 free((genericptr_t) sysopt.debugfiles);
2278 sysopt.debugfiles = dupstr(bufp);
2280 } else if (src == SET_IN_SYS && match_varname(buf, "SUPPORT", 7)) {
2282 free((genericptr_t) sysopt.support);
2283 sysopt.support = dupstr(bufp);
2284 } else if (src == SET_IN_SYS && match_varname(buf, "RECOVER", 7)) {
2286 free((genericptr_t) sysopt.recover);
2287 sysopt.recover = dupstr(bufp);
2288 } else if (src == SET_IN_SYS
2289 && match_varname(buf, "CHECK_SAVE_UID", 14)) {
2291 sysopt.check_save_uid = n;
2292 } else if (match_varname(buf, "SEDUCE", 6)) {
2293 n = !!atoi(bufp); /* XXX this could be tighter */
2294 /* allow anyone to turn it off, but only sysconf to turn it on*/
2295 if (src != SET_IN_SYS && n != 0) {
2296 raw_printf("Illegal value in SEDUCE");
2300 sysopt_seduce_set(sysopt.seduce);
2301 } else if (src == SET_IN_SYS && match_varname(buf, "MAXPLAYERS", 10)) {
2303 /* XXX to get more than 25, need to rewrite all lock code */
2304 if (n < 1 || n > 25) {
2305 raw_printf("Illegal value in MAXPLAYERS (maximum is 25).");
2308 sysopt.maxplayers = n;
2309 } else if (src == SET_IN_SYS && match_varname(buf, "PERSMAX", 7)) {
2312 raw_printf("Illegal value in PERSMAX (minimum is 1).");
2316 } else if (src == SET_IN_SYS && match_varname(buf, "PERS_IS_UID", 11)) {
2318 if (n != 0 && n != 1) {
2319 raw_printf("Illegal value in PERS_IS_UID (must be 0 or 1).");
2322 sysopt.pers_is_uid = n;
2323 } else if (src == SET_IN_SYS && match_varname(buf, "ENTRYMAX", 8)) {
2326 raw_printf("Illegal value in ENTRYMAX (minimum is 10).");
2329 sysopt.entrymax = n;
2330 } else if ((src == SET_IN_SYS) && match_varname(buf, "POINTSMIN", 9)) {
2333 raw_printf("Illegal value in POINTSMIN (minimum is 1).");
2336 sysopt.pointsmin = n;
2337 } else if (src == SET_IN_SYS
2338 && match_varname(buf, "MAX_STATUENAME_RANK", 10)) {
2342 "Illegal value in MAX_STATUENAME_RANK (minimum is 1).");
2345 sysopt.tt_oname_maxrank = n;
2347 /* SYSCF PANICTRACE options */
2348 } else if (src == SET_IN_SYS
2349 && match_varname(buf, "PANICTRACE_LIBC", 15)) {
2351 #if defined(PANICTRACE) && defined(PANICTRACE_LIBC)
2352 if (n < 0 || n > 2) {
2353 raw_printf("Illegal value in PANICTRACE_LIBC (not 0,1,2).");
2357 sysopt.panictrace_libc = n;
2358 } else if (src == SET_IN_SYS
2359 && match_varname(buf, "PANICTRACE_GDB", 14)) {
2361 #if defined(PANICTRACE)
2362 if (n < 0 || n > 2) {
2363 raw_printf("Illegal value in PANICTRACE_GDB (not 0,1,2).");
2367 sysopt.panictrace_gdb = n;
2368 } else if (src == SET_IN_SYS && match_varname(buf, "GDBPATH", 7)) {
2369 #if defined(PANICTRACE) && !defined(VMS)
2370 if (!file_exists(bufp)) {
2371 raw_printf("File specified in GDBPATH does not exist.");
2376 free((genericptr_t) sysopt.gdbpath);
2377 sysopt.gdbpath = dupstr(bufp);
2378 } else if (src == SET_IN_SYS && match_varname(buf, "GREPPATH", 7)) {
2379 #if defined(PANICTRACE) && !defined(VMS)
2380 if (!file_exists(bufp)) {
2381 raw_printf("File specified in GREPPATH does not exist.");
2385 if (sysopt.greppath)
2386 free((genericptr_t) sysopt.greppath);
2387 sysopt.greppath = dupstr(bufp);
2390 } else if (match_varname(buf, "BOULDER", 3)) {
2391 (void) get_uchars(fp, buf, bufp, &iflags.bouldersym, TRUE, 1,
2393 } else if (match_varname(buf, "MENUCOLOR", 9)) {
2394 (void) add_menu_coloring(bufp);
2395 } else if (match_varname(buf, "WARNINGS", 5)) {
2396 (void) get_uchars(fp, buf, bufp, translate, FALSE, WARNCOUNT,
2398 assign_warnings(translate);
2399 } else if (match_varname(buf, "SYMBOLS", 4)) {
2400 char *op, symbuf[BUFSZ];
2404 /* check for line continuation (trailing '\') */
2406 morelines = (--op >= bufp && *op == '\\');
2409 /* strip trailing space now that '\' is gone */
2410 if (--op >= bufp && *op == ' ')
2418 if (!fgets(symbuf, BUFSZ, fp)) {
2424 } while (*bufp == '#');
2426 } while (morelines);
2427 switch_symbols(TRUE);
2428 } else if (match_varname(buf, "WIZKIT", 6)) {
2429 (void) strncpy(wizkit, bufp, WIZKIT_MAX - 1);
2431 } else if (match_varname(buf, "FONT", 4)) {
2434 if (t = strchr(buf + 5, ':')) {
2436 amii_set_text_font(buf + 5, atoi(t + 1));
2439 } else if (match_varname(buf, "PATH", 4)) {
2440 (void) strncpy(PATH, bufp, PATHLEN - 1);
2441 } else if (match_varname(buf, "DEPTH", 5)) {
2442 extern int amii_numcolors;
2443 int val = atoi(bufp);
2445 amii_numcolors = 1L << min(DEPTH, val);
2447 } else if (match_varname(buf, "DRIPENS", 7)) {
2451 for (i = 0, t = strtok(bufp, ",/"); t != (char *) 0;
2452 i < 20 && (t = strtok((char *) 0, ",/")), ++i) {
2453 sscanf(t, "%d", &val);
2454 sysflags.amii_dripens[i] = val;
2457 } else if (match_varname(buf, "SCREENMODE", 10)) {
2458 extern long amii_scrnmode;
2460 if (!stricmp(bufp, "req"))
2461 amii_scrnmode = 0xffffffff; /* Requester */
2462 else if (sscanf(bufp, "%x", &amii_scrnmode) != 1)
2464 } else if (match_varname(buf, "MSGPENS", 7)) {
2465 extern int amii_msgAPen, amii_msgBPen;
2466 char *t = strtok(bufp, ",/");
2469 sscanf(t, "%d", &amii_msgAPen);
2470 if (t = strtok((char *) 0, ",/"))
2471 sscanf(t, "%d", &amii_msgBPen);
2473 } else if (match_varname(buf, "TEXTPENS", 8)) {
2474 extern int amii_textAPen, amii_textBPen;
2475 char *t = strtok(bufp, ",/");
2478 sscanf(t, "%d", &amii_textAPen);
2479 if (t = strtok((char *) 0, ",/"))
2480 sscanf(t, "%d", &amii_textBPen);
2482 } else if (match_varname(buf, "MENUPENS", 8)) {
2483 extern int amii_menuAPen, amii_menuBPen;
2484 char *t = strtok(bufp, ",/");
2487 sscanf(t, "%d", &amii_menuAPen);
2488 if (t = strtok((char *) 0, ",/"))
2489 sscanf(t, "%d", &amii_menuBPen);
2491 } else if (match_varname(buf, "STATUSPENS", 10)) {
2492 extern int amii_statAPen, amii_statBPen;
2493 char *t = strtok(bufp, ",/");
2496 sscanf(t, "%d", &amii_statAPen);
2497 if (t = strtok((char *) 0, ",/"))
2498 sscanf(t, "%d", &amii_statBPen);
2500 } else if (match_varname(buf, "OTHERPENS", 9)) {
2501 extern int amii_otherAPen, amii_otherBPen;
2502 char *t = strtok(bufp, ",/");
2505 sscanf(t, "%d", &amii_otherAPen);
2506 if (t = strtok((char *) 0, ",/"))
2507 sscanf(t, "%d", &amii_otherBPen);
2509 } else if (match_varname(buf, "PENS", 4)) {
2510 extern unsigned short amii_init_map[AMII_MAXCOLORS];
2514 for (i = 0, t = strtok(bufp, ",/");
2515 i < AMII_MAXCOLORS && t != (char *) 0;
2516 t = strtok((char *) 0, ",/"), ++i) {
2517 sscanf(t, "%hx", &amii_init_map[i]);
2519 amii_setpens(amii_numcolors = i);
2520 } else if (match_varname(buf, "FGPENS", 6)) {
2521 extern int foreg[AMII_MAXCOLORS];
2525 for (i = 0, t = strtok(bufp, ",/");
2526 i < AMII_MAXCOLORS && t != (char *) 0;
2527 t = strtok((char *) 0, ",/"), ++i) {
2528 sscanf(t, "%d", &foreg[i]);
2530 } else if (match_varname(buf, "BGPENS", 6)) {
2531 extern int backg[AMII_MAXCOLORS];
2535 for (i = 0, t = strtok(bufp, ",/");
2536 i < AMII_MAXCOLORS && t != (char *) 0;
2537 t = strtok((char *) 0, ",/"), ++i) {
2538 sscanf(t, "%d", &backg[i]);
2542 } else if (match_varname(buf, "SOUNDDIR", 8)) {
2543 sounddir = dupstr(bufp);
2544 } else if (match_varname(buf, "SOUND", 5)) {
2545 add_sound_mapping(bufp);
2548 /* These should move to wc_ options */
2549 } else if (match_varname(buf, "QT_TILEWIDTH", 12)) {
2550 extern char *qt_tilewidth;
2552 if (qt_tilewidth == NULL)
2553 qt_tilewidth = dupstr(bufp);
2554 } else if (match_varname(buf, "QT_TILEHEIGHT", 13)) {
2555 extern char *qt_tileheight;
2557 if (qt_tileheight == NULL)
2558 qt_tileheight = dupstr(bufp);
2559 } else if (match_varname(buf, "QT_FONTSIZE", 11)) {
2560 extern char *qt_fontsize;
2562 if (qt_fontsize == NULL)
2563 qt_fontsize = dupstr(bufp);
2564 } else if (match_varname(buf, "QT_COMPACT", 10)) {
2565 extern int qt_compact_mode;
2567 qt_compact_mode = atoi(bufp);
2576 can_read_file(filename)
2577 const char *filename;
2579 return (boolean) (access(filename, 4) == 0);
2581 #endif /* USER_SOUNDS */
2584 read_config_file(filename, src)
2585 const char *filename;
2588 char buf[4 * BUFSZ], *p;
2590 boolean rv = TRUE; /* assume successful parse */
2592 if (!(fp = fopen_config_file(filename, src)))
2595 /* begin detection of duplicate configfile options */
2596 set_duplicate_opt_detection(1);
2598 while (fgets(buf, sizeof buf, fp)) {
2601 XXX Don't call read() in parse_config_line, read as callback or reassemble
2603 OR: Forbid multiline stuff for alternate config sources.
2606 if ((p = index(buf, '\n')) != 0)
2608 if (!parse_config_line(fp, buf, src)) {
2609 static const char badoptionline[] = "Bad option line: \"%s\"";
2611 /* truncate buffer if it's long; this is actually conservative */
2612 if (strlen(buf) > BUFSZ - sizeof badoptionline)
2613 buf[BUFSZ - sizeof badoptionline] = '\0';
2615 raw_printf(badoptionline, buf);
2622 /* turn off detection of duplicate configfile options */
2623 set_duplicate_opt_detection(0);
2631 #if defined(VMS) || defined(UNIX)
2632 char tmp_wizkit[BUFSZ];
2636 envp = nh_getenv("WIZKIT");
2638 (void) strncpy(wizkit, envp, WIZKIT_MAX - 1);
2643 if (access(wizkit, 4) == -1) {
2644 /* 4 is R_OK on newer systems */
2645 /* nasty sneaky attempt to read file through
2646 * NetHack's setuid permissions -- this is a
2647 * place a file name may be wholly under the player's
2650 raw_printf("Access to %s denied (%d).", wizkit, errno);
2652 /* fall through to standard names */
2655 if ((fp = fopenp(wizkit, "r")) != (FILE *) 0) {
2657 #if defined(UNIX) || defined(VMS)
2659 /* access() above probably caught most problems for UNIX */
2660 raw_printf("Couldn't open requested config file %s (%d).", wizkit,
2666 #if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32)
2667 if ((fp = fopenp(fqname(wizkit, CONFIGPREFIX, 0), "r")) != (FILE *) 0)
2671 envp = nh_getenv("HOME");
2673 Sprintf(tmp_wizkit, "%s%s", envp, wizkit);
2675 Sprintf(tmp_wizkit, "%s%s", "sys$login:", wizkit);
2676 if ((fp = fopenp(tmp_wizkit, "r")) != (FILE *) 0)
2678 #else /* should be only UNIX left */
2679 envp = nh_getenv("HOME");
2681 Sprintf(tmp_wizkit, "%s/%s", envp, wizkit);
2683 Strcpy(tmp_wizkit, wizkit);
2684 if ((fp = fopenp(tmp_wizkit, "r")) != (FILE *) 0)
2686 else if (errno != ENOENT) {
2687 /* e.g., problems when setuid NetHack can't search home
2688 * directory restricted to user */
2689 raw_printf("Couldn't open default wizkit file %s (%d).", tmp_wizkit,
2698 /* add to hero's inventory if there's room, otherwise put item on floor */
2703 if (!obj || obj == &zeroobj)
2706 /* subset of starting inventory pre-ID */
2708 if (Role_if(PM_PRIEST))
2710 /* same criteria as lift_object()'s check for available inventory slot */
2711 if (obj->oclass != COIN_CLASS && inv_cnt(FALSE) >= 52
2712 && !merge_choice(invent, obj)) {
2713 /* inventory overflow; can't just place & stack object since
2714 hero isn't in position yet, so schedule for arrival later */
2715 add_to_migration(obj);
2716 obj->ox = 0; /* index of main dungeon */
2717 obj->oy = 1; /* starting level number */
2719 (long) (MIGR_WITH_HERO | MIGR_NOBREAK | MIGR_NOSCATTER);
2729 char *ep, buf[BUFSZ];
2731 boolean bad_items = FALSE, skip = FALSE;
2733 if (!wizard || !(fp = fopen_wizkit_file()))
2736 program_state.wizkit_wishing = 1;
2737 while (fgets(buf, (int) (sizeof buf), fp)) {
2738 ep = index(buf, '\n');
2739 if (skip) { /* in case previous line was too long */
2741 skip = FALSE; /* found newline; next line is normal */
2744 skip = TRUE; /* newline missing; discard next fgets */
2746 *ep = '\0'; /* remove newline */
2749 otmp = readobjnam(buf, (struct obj *) 0);
2751 if (otmp != &zeroobj)
2752 wizkit_addinv(otmp);
2754 /* .60 limits output line width to 79 chars */
2755 raw_printf("Bad wizkit item: \"%.60s\"", buf);
2761 program_state.wizkit_wishing = 0;
2768 extern struct symsetentry *symset_list; /* options.c */
2769 extern struct symparse loadsyms[]; /* drawing.c */
2770 extern const char *known_handling[]; /* drawing.c */
2771 extern const char *known_restrictions[]; /* drawing.c */
2772 static int symset_count = 0; /* for pick-list building only */
2773 static boolean chosen_symset_start = FALSE, chosen_symset_end = FALSE;
2781 fp = fopen_datafile(SYMBOLS, "r", HACKPREFIX);
2786 * Returns 1 if the chose symset was found and loaded.
2787 * 0 if it wasn't found in the sym file or other problem.
2790 read_sym_file(which_set)
2793 char buf[4 * BUFSZ];
2796 if (!(fp = fopen_sym_file()))
2800 chosen_symset_start = chosen_symset_end = FALSE;
2801 while (fgets(buf, 4 * BUFSZ, fp)) {
2802 if (!parse_sym_line(buf, which_set)) {
2803 raw_printf("Bad symbol line: \"%.50s\"", buf);
2808 if (!chosen_symset_end && !chosen_symset_start)
2809 return (symset[which_set].name == 0) ? 1 : 0;
2810 if (!chosen_symset_end) {
2811 raw_printf("Missing finish for symset \"%s\"",
2812 symset[which_set].name ? symset[which_set].name
2819 /* returns 0 on error */
2821 parse_sym_line(buf, which_set)
2826 struct symparse *symp = (struct symparse *) 0;
2827 char *bufp, *commentp, *altp;
2829 /* convert each instance of whitespace (tabs, consecutive spaces)
2830 into a single space; leading and trailing spaces are stripped */
2832 if (!*buf || *buf == '#' || !strcmp(buf, " "))
2834 /* remove trailing comment, if any */
2835 if ((commentp = rindex(buf, '#')) != 0) {
2837 /* remove space preceding the stripped comment, if any;
2838 we know 'commentp > buf' because *buf=='#' was caught above */
2839 if (commentp[-1] == ' ')
2843 /* find the '=' or ':' */
2844 bufp = index(buf, '=');
2845 altp = index(buf, ':');
2846 if (!bufp || (altp && altp < bufp))
2849 if (strncmpi(buf, "finish", 6) == 0) {
2850 /* end current graphics set */
2851 if (chosen_symset_start)
2852 chosen_symset_end = TRUE;
2853 chosen_symset_start = FALSE;
2858 /* skip '=' and space which follows, if any */
2863 symp = match_sym(buf);
2867 if (!symset[which_set].name) {
2868 /* A null symset name indicates that we're just
2869 building a pick-list of possible symset
2870 values from the file, so only do that */
2871 if (symp->range == SYM_CONTROL) {
2872 struct symsetentry *tmpsp;
2874 switch (symp->idx) {
2877 (struct symsetentry *) alloc(sizeof (struct symsetentry));
2878 tmpsp->next = (struct symsetentry *) 0;
2880 symset_list = tmpsp;
2884 tmpsp->next = symset_list;
2885 symset_list = tmpsp;
2887 tmpsp->idx = symset_count;
2888 tmpsp->name = dupstr(bufp);
2889 tmpsp->desc = (char *) 0;
2891 /* initialize restriction bits */
2896 /* handler type identified */
2897 tmpsp = symset_list; /* most recent symset */
2898 tmpsp->handling = H_UNK;
2900 while (known_handling[i]) {
2901 if (!strcmpi(known_handling[i], bufp)) {
2902 tmpsp->handling = i;
2903 break; /* while loop */
2908 case 3: /* description:something */
2909 tmpsp = symset_list; /* most recent symset */
2910 if (tmpsp && !tmpsp->desc)
2911 tmpsp->desc = dupstr(bufp);
2914 /* restrictions: xxxx*/
2915 tmpsp = symset_list; /* most recent symset */
2916 for (i = 0; known_restrictions[i]; ++i) {
2917 if (!strcmpi(known_restrictions[i], bufp)) {
2926 break; /* while loop */
2935 if (symp->range == SYM_CONTROL) {
2936 switch (symp->idx) {
2938 /* start of symset */
2939 if (!strcmpi(bufp, symset[which_set].name)) {
2940 /* matches desired one */
2941 chosen_symset_start = TRUE;
2942 /* these init_*() functions clear symset fields too */
2943 if (which_set == ROGUESET)
2945 else if (which_set == PRIMARY)
2951 if (chosen_symset_start)
2952 chosen_symset_end = TRUE;
2953 chosen_symset_start = FALSE;
2956 /* handler type identified */
2957 if (chosen_symset_start)
2958 set_symhandling(bufp, which_set);
2960 /* case 3: (description) is ignored here */
2961 case 4: /* color:off */
2962 if (chosen_symset_start) {
2964 if (!strcmpi(bufp, "true") || !strcmpi(bufp, "yes")
2965 || !strcmpi(bufp, "on"))
2966 symset[which_set].nocolor = 0;
2967 else if (!strcmpi(bufp, "false")
2968 || !strcmpi(bufp, "no")
2969 || !strcmpi(bufp, "off"))
2970 symset[which_set].nocolor = 1;
2974 case 5: /* restrictions: xxxx*/
2975 if (chosen_symset_start) {
2978 while (known_restrictions[n]) {
2979 if (!strcmpi(known_restrictions[n], bufp)) {
2982 symset[which_set].primary = 1;
2985 symset[which_set].rogue = 1;
2988 break; /* while loop */
2995 } else { /* !SYM_CONTROL */
2996 val = sym_val(bufp);
2997 if (chosen_symset_start) {
2998 if (which_set == PRIMARY) {
2999 update_l_symset(symp, val);
3000 } else if (which_set == ROGUESET) {
3001 update_r_symset(symp, val);
3010 set_symhandling(handling, which_set)
3016 symset[which_set].handling = H_UNK;
3017 while (known_handling[i]) {
3018 if (!strcmpi(known_handling[i], handling)) {
3019 symset[which_set].handling = i;
3026 /* ---------- END CONFIG FILE HANDLING ----------- */
3028 /* ---------- BEGIN SCOREBOARD CREATION ----------- */
3031 #define UNUSED_if_not_OS2_CODEVIEW /*empty*/
3033 #define UNUSED_if_not_OS2_CODEVIEW UNUSED
3036 /* verify that we can write to scoreboard file; if not, try to create one */
3039 check_recordfile(dir)
3040 const char *dir UNUSED_if_not_OS2_CODEVIEW;
3042 #if defined(PRAGMA_UNUSED) && !defined(OS2_CODEVIEW)
3045 const char *fq_record;
3048 #if defined(UNIX) || defined(VMS)
3049 fq_record = fqname(RECORD, SCOREPREFIX, 0);
3050 fd = open(fq_record, O_RDWR, 0);
3052 #ifdef VMS /* must be stream-lf to use UPDATE_RECORD_IN_PLACE */
3053 if (!file_is_stmlf(fd)) {
3055 "Warning: scoreboard file %s is not in stream_lf format",
3060 (void) nhclose(fd); /* RECORD is accessible */
3061 } else if ((fd = open(fq_record, O_CREAT | O_RDWR, FCMASK)) >= 0) {
3062 (void) nhclose(fd); /* RECORD newly created */
3063 #if defined(VMS) && !defined(SECURE)
3064 /* Re-protect RECORD with world:read+write+execute+delete access. */
3065 (void) chmod(fq_record, FCMASK | 007);
3066 #endif /* VMS && !SECURE */
3068 raw_printf("Warning: cannot write scoreboard file %s", fq_record);
3071 #endif /* !UNIX && !VMS */
3072 #if defined(MICRO) || defined(WIN32)
3075 #ifdef OS2_CODEVIEW /* explicit path on opening for OS/2 */
3076 /* how does this work when there isn't an explicit path or fopenp
3077 * for later access to the file via fopen_datafile? ? */
3078 (void) strncpy(tmp, dir, PATHLEN - 1);
3079 tmp[PATHLEN - 1] = '\0';
3080 if ((strlen(tmp) + 1 + strlen(RECORD)) < (PATHLEN - 1)) {
3082 Strcat(tmp, RECORD);
3086 Strcpy(tmp, RECORD);
3087 fq_record = fqname(RECORD, SCOREPREFIX, 0);
3090 if ((fd = open(fq_record, O_RDWR)) < 0) {
3091 /* try to create empty record */
3092 #if defined(AZTEC_C) || defined(_DCC) \
3093 || (defined(__GNUC__) && defined(__AMIGA__))
3094 /* Aztec doesn't use the third argument */
3095 /* DICE doesn't like it */
3096 if ((fd = open(fq_record, O_CREAT | O_RDWR)) < 0) {
3098 if ((fd = open(fq_record, O_CREAT | O_RDWR, S_IREAD | S_IWRITE))
3101 raw_printf("Warning: cannot write record %s", tmp);
3105 } else /* open succeeded */
3107 #else /* MICRO || WIN32*/
3110 /* Create the "record" file, if necessary */
3111 fq_record = fqname(RECORD, SCOREPREFIX, 0);
3112 fd = macopen(fq_record, O_RDWR | O_CREAT, TEXT_TYPE);
3117 #endif /* MICRO || WIN32*/
3120 /* ---------- END SCOREBOARD CREATION ----------- */
3122 /* ---------- BEGIN PANIC/IMPOSSIBLE LOG ----------- */
3126 paniclog(type, reason)
3127 const char *type; /* panic, impossible, trickery */
3128 const char *reason; /* explanation */
3134 if (!program_state.in_paniclog) {
3135 program_state.in_paniclog = 1;
3136 lfile = fopen_datafile(PANICLOG, "a", TROUBLEPREFIX);
3138 time_t now = getnow();
3140 char playmode = wizard ? 'D' : discover ? 'X' : '-';
3142 (void) fprintf(lfile, "%s %08ld %06ld %d %c: %s %s\n",
3143 version_string(buf), yyyymmdd(now), hhmmss(now),
3144 uid, playmode, type, reason);
3145 (void) fclose(lfile);
3147 program_state.in_paniclog = 0;
3149 #endif /* PANICLOG */
3153 /* ---------- END PANIC/IMPOSSIBLE LOG ----------- */
3157 /* ---------- BEGIN INTERNAL RECOVER ----------- */
3162 int lev, savelev, hpid, pltmpsiz;
3164 struct version_info version_data;
3166 char savename[SAVESIZE], errbuf[BUFSZ];
3167 struct savefile_info sfi;
3168 char tmpplbuf[PL_NSIZ];
3170 for (lev = 0; lev < 256; lev++)
3173 /* level 0 file contains:
3174 * pid of creating process (ignored here)
3175 * level number for current level of save file
3176 * name of save file nethack would have created
3181 gfd = open_levelfile(0, errbuf);
3183 raw_printf("%s\n", errbuf);
3186 if (read(gfd, (genericptr_t) &hpid, sizeof hpid) != sizeof hpid) {
3187 raw_printf("\n%s\n%s\n",
3188 "Checkpoint data incompletely written or subsequently clobbered.",
3189 "Recovery impossible.");
3190 (void) nhclose(gfd);
3193 if (read(gfd, (genericptr_t) &savelev, sizeof(savelev))
3194 != sizeof(savelev)) {
3196 "\nCheckpointing was not in effect for %s -- recovery impossible.\n",
3198 (void) nhclose(gfd);
3201 if ((read(gfd, (genericptr_t) savename, sizeof savename)
3203 || (read(gfd, (genericptr_t) &version_data, sizeof version_data)
3204 != sizeof version_data)
3205 || (read(gfd, (genericptr_t) &sfi, sizeof sfi) != sizeof sfi)
3206 || (read(gfd, (genericptr_t) &pltmpsiz, sizeof pltmpsiz)
3207 != sizeof pltmpsiz) || (pltmpsiz > PL_NSIZ)
3208 || (read(gfd, (genericptr_t) &tmpplbuf, pltmpsiz) != pltmpsiz)) {
3209 raw_printf("\nError reading %s -- can't recover.\n", lock);
3210 (void) nhclose(gfd);
3214 /* save file should contain:
3218 * current level (including pets)
3219 * (non-level-based) game state
3222 set_savefile_name(TRUE);
3223 sfd = create_savefile();
3225 raw_printf("\nCannot recover savefile %s.\n", SAVEF);
3226 (void) nhclose(gfd);
3230 lfd = open_levelfile(savelev, errbuf);
3232 raw_printf("\n%s\n", errbuf);
3233 (void) nhclose(gfd);
3234 (void) nhclose(sfd);
3239 if (write(sfd, (genericptr_t) &version_data, sizeof version_data)
3240 != sizeof version_data) {
3241 raw_printf("\nError writing %s; recovery failed.", SAVEF);
3242 (void) nhclose(gfd);
3243 (void) nhclose(sfd);
3248 if (write(sfd, (genericptr_t) &sfi, sizeof sfi) != sizeof sfi) {
3249 raw_printf("\nError writing %s; recovery failed (savefile_info).\n",
3251 (void) nhclose(gfd);
3252 (void) nhclose(sfd);
3257 if (write(sfd, (genericptr_t) &pltmpsiz, sizeof pltmpsiz)
3258 != sizeof pltmpsiz) {
3259 raw_printf("Error writing %s; recovery failed (player name size).\n",
3261 (void) nhclose(gfd);
3262 (void) nhclose(sfd);
3267 if (write(sfd, (genericptr_t) &tmpplbuf, pltmpsiz) != pltmpsiz) {
3268 raw_printf("Error writing %s; recovery failed (player name).\n",
3270 (void) nhclose(gfd);
3271 (void) nhclose(sfd);
3276 if (!copy_bytes(lfd, sfd)) {
3277 (void) nhclose(lfd);
3278 (void) nhclose(sfd);
3282 (void) nhclose(lfd);
3283 processed[savelev] = 1;
3285 if (!copy_bytes(gfd, sfd)) {
3286 (void) nhclose(lfd);
3287 (void) nhclose(sfd);
3291 (void) nhclose(gfd);
3294 for (lev = 1; lev < 256; lev++) {
3295 /* level numbers are kept in xchars in save.c, so the
3296 * maximum level number (for the endlevel) must be < 256
3298 if (lev != savelev) {
3299 lfd = open_levelfile(lev, (char *) 0);
3301 /* any or all of these may not exist */
3303 write(sfd, (genericptr_t) &levc, sizeof(levc));
3304 if (!copy_bytes(lfd, sfd)) {
3305 (void) nhclose(lfd);
3306 (void) nhclose(sfd);
3310 (void) nhclose(lfd);
3315 (void) nhclose(sfd);
3317 #ifdef HOLD_LOCKFILE_OPEN
3321 * We have a successful savefile!
3322 * Only now do we erase the level files.
3324 for (lev = 0; lev < 256; lev++) {
3325 if (processed[lev]) {
3326 const char *fq_lock;
3327 set_levelfile_name(lock, lev);
3328 fq_lock = fqname(lock, LEVELPREFIX, 3);
3329 (void) unlink(fq_lock);
3336 copy_bytes(ifd, ofd)
3343 nfrom = read(ifd, buf, BUFSIZ);
3344 nto = write(ofd, buf, nfrom);
3347 } while (nfrom == BUFSIZ);
3351 /* ---------- END INTERNAL RECOVER ----------- */
3352 #endif /*SELF_RECOVER*/
3354 /* ---------- OTHER ----------- */
3364 * All we really care about is the end result - can we read the file?
3365 * So just check that directly.
3367 * Not tested on most of the old platforms (which don't attempt
3368 * to implement SYSCF).
3369 * Some ports don't like open()'s optional third argument;
3370 * VMS overrides open() usage with a macro which requires it.
3373 fd = open(SYSCF_FILE, O_RDONLY);
3375 fd = open(SYSCF_FILE, O_RDONLY, 0);
3382 raw_printf("Unable to open SYSCF_FILE.\n");
3386 #endif /* SYSCF_FILE */
3390 /* used by debugpline() to decide whether to issue a message
3391 * from a particular source file; caller passes __FILE__ and we check
3392 * whether it is in the source file list supplied by SYSCF's DEBUGFILES
3394 * pass FALSE to override wildcard matching; useful for files
3395 * like dungeon.c and questpgr.c, which generate a ridiculous amount of
3396 * output if DEBUG is defined and effectively block the use of a wildcard */
3398 debugcore(filename, wildcards)
3399 const char *filename;
3402 const char *debugfiles, *p;
3404 if (!filename || !*filename)
3405 return FALSE; /* sanity precaution */
3407 if (sysopt.env_dbgfl == 0) {
3408 /* check once for DEBUGFILES in the environment;
3409 if found, it supersedes the sysconf value
3410 [note: getenv() rather than nh_getenv() since a long value
3411 is valid and doesn't pose any sort of overflow risk here] */
3412 if ((p = getenv("DEBUGFILES")) != 0) {
3413 if (sysopt.debugfiles)
3414 free((genericptr_t) sysopt.debugfiles);
3415 sysopt.debugfiles = dupstr(p);
3416 sysopt.env_dbgfl = 1;
3418 sysopt.env_dbgfl = -1;
3421 debugfiles = sysopt.debugfiles;
3422 /* usual case: sysopt.debugfiles will be empty */
3423 if (!debugfiles || !*debugfiles)
3426 /* strip filename's path if present */
3428 if ((p = rindex(filename, '/')) != 0)
3432 filename = vms_basename(filename);
3433 /* vms_basename strips off 'type' suffix as well as path and version;
3434 we want to put suffix back (".c" assumed); since it always returns
3435 a pointer to a static buffer, we can safely modify its result */
3436 Strcat((char *) filename, ".c");
3440 * Wildcard match will only work if there's a single pattern (which
3441 * might be a single file name without any wildcarding) rather than
3442 * a space-separated list.
3443 * [to NOT do: We could step through the space-separated list and
3444 * attempt a wildcard match against each element, but that would be
3445 * overkill for the intended usage.]
3447 if (wildcards && pmatch(debugfiles, filename))
3450 /* check whether filename is an element of the list */
3451 if ((p = strstr(debugfiles, filename)) != 0) {
3452 int l = (int) strlen(filename);
3454 if ((p == debugfiles || p[-1] == ' ' || p[-1] == '/')
3455 && (p[l] == ' ' || p[l] == '\0'))
3463 /* ---------- BEGIN TRIBUTE ----------- */
3468 #define SECTIONSCOPE 1
3469 #define TITLESCOPE 2
3470 #define PASSAGESCOPE 3
3472 #define MAXPASSAGES SIZE(context.novel.pasg) /* 30 */
3474 static int FDECL(choose_passage, (int, unsigned));
3476 /* choose a random passage that hasn't been chosen yet; once all have
3477 been chosen, reset the tracking to make all passages available again */
3479 choose_passage(passagecnt, oid)
3480 int passagecnt; /* total of available passages, 1..MAXPASSAGES */
3481 unsigned oid; /* book.o_id, used to determine whether re-reading same book */
3487 if (passagecnt > MAXPASSAGES)
3488 passagecnt = MAXPASSAGES;
3490 /* if a different book or we've used up all the passages already,
3491 reset in order to have all 'passagecnt' passages available */
3492 if (oid != context.novel.id || context.novel.count == 0) {
3493 context.novel.id = oid;
3494 context.novel.count = passagecnt;
3495 for (idx = 0; idx < MAXPASSAGES; idx++)
3496 context.novel.pasg[idx] = (xchar) ((idx < passagecnt) ? idx + 1
3500 idx = rn2(context.novel.count);
3501 res = (int) context.novel.pasg[idx];
3502 /* move the last slot's passage index into the slot just used
3503 and reduce the number of passages available */
3504 context.novel.pasg[idx] = context.novel.pasg[--context.novel.count];
3508 /* Returns True if you were able to read something. */
3510 read_tribute(tribsection, tribtitle, tribpassage, nowin_buf, bufsz, oid)
3511 const char *tribsection, *tribtitle;
3512 int tribpassage, bufsz;
3514 unsigned oid; /* book identifier */
3518 char line[BUFSZ], lastline[BUFSZ];
3521 int linect = 0, passagecnt = 0, targetpassage = 0;
3522 const char *badtranslation = "an incomprehensible foreign translation";
3523 boolean matchedsection = FALSE, matchedtitle = FALSE;
3524 winid tribwin = WIN_ERR;
3525 boolean grasped = FALSE;
3526 boolean foundpassage = FALSE;
3528 /* check for mandatories */
3529 if (!tribsection || !tribtitle) {
3531 pline("It's %s of \"%s\"!", badtranslation, tribtitle);
3535 debugpline3("read_tribute %s, %s, %d.", tribsection, tribtitle,
3538 fp = dlb_fopen(TRIBUTEFILE, "r");
3540 /* this is actually an error - cannot open tribute file! */
3542 pline("You feel too overwhelmed to continue!");
3547 * Syntax (not case-sensitive):
3550 * In the books section:
3551 * %title booktitle (n)
3552 * where booktitle=book title without quotes
3553 * (n)= total number of passages present for this title
3555 * where k=sequential passage number
3557 * %e ends the passage/book/section
3558 * If in a passage, it marks the end of that passage.
3559 * If in a book, it marks the end of that book.
3560 * If in a section, it marks the end of that section.
3565 *line = *lastline = '\0';
3566 while (dlb_fgets(line, sizeof line, fp) != 0) {
3568 if ((endp = index(line, '\n')) != 0)
3572 if (!strncmpi(&line[1], "section ", sizeof("section ") - 1)) {
3573 char *st = &line[9]; /* 9 from "%section " */
3575 scope = SECTIONSCOPE;
3576 if (!strcmpi(st, tribsection))
3577 matchedsection = TRUE;
3579 matchedsection = FALSE;
3580 } else if (!strncmpi(&line[1], "title ", sizeof("title ") - 1)) {
3581 char *st = &line[7]; /* 7 from "%title " */
3584 if ((p1 = index(st, '(')) != 0) {
3586 (void) mungspaces(st);
3587 if ((p2 = index(p1, ')')) != 0) {
3589 passagecnt = atoi(p1);
3590 if (passagecnt > MAXPASSAGES)
3591 passagecnt = MAXPASSAGES;
3593 if (matchedsection && !strcmpi(st, tribtitle)) {
3594 matchedtitle = TRUE;
3595 targetpassage = !tribpassage
3596 ? choose_passage(passagecnt, oid)
3597 : (tribpassage <= passagecnt)
3600 matchedtitle = FALSE;
3604 } else if (!strncmpi(&line[1], "passage ",
3605 sizeof("passage ") - 1)) {
3607 char *st = &line[9]; /* 9 from "%passage " */
3609 while (*st == ' ' || *st == '\t')
3611 if (*st && digit(*st) && (strlen(st) < 3))
3612 passagenum = atoi(st);
3613 if (passagenum && (passagenum <= passagecnt)) {
3614 scope = PASSAGESCOPE;
3615 if (matchedtitle && (passagenum == targetpassage)) {
3617 tribwin = create_nhwindow(NHW_MENU);
3619 foundpassage = TRUE;
3622 } else if (!strncmpi(&line[1], "e ", sizeof("e ") - 1)) {
3623 if (matchedtitle && scope == PASSAGESCOPE
3624 && ((!nowin_buf && tribwin != WIN_ERR)
3625 || (nowin_buf && foundpassage)))
3627 if (scope == TITLESCOPE)
3628 matchedtitle = FALSE;
3629 if (scope == SECTIONSCOPE)
3630 matchedsection = FALSE;
3634 debugpline1("tribute file error: bad %% command, line %d.",
3639 /* comment only, next! */
3642 if (matchedtitle && scope == PASSAGESCOPE) {
3643 if (!nowin_buf && tribwin != WIN_ERR) {
3644 putstr(tribwin, 0, line);
3645 Strcpy(lastline, line);
3646 } else if (nowin_buf) {
3647 if ((int) strlen(line) < bufsz - 1)
3648 Strcpy(nowin_buf, line);
3655 (void) dlb_fclose(fp);
3656 if (!nowin_buf && tribwin != WIN_ERR) {
3657 if (matchedtitle && scope == PASSAGESCOPE) {
3658 display_nhwindow(tribwin, FALSE);
3659 /* put the final attribution line into message history,
3660 analogous to the summary line from long quest messages */
3661 if (index(lastline, '['))
3662 mungspaces(lastline); /* to remove leading spaces */
3663 else /* construct one if necessary */
3664 Sprintf(lastline, "[%s, by Terry Pratchett]", tribtitle);
3665 putmsghistory(lastline, FALSE);
3667 destroy_nhwindow(tribwin);
3672 pline("It seems to be %s of \"%s\"!", badtranslation, tribtitle);
3681 Death_quote(buf, bufsz)
3685 unsigned death_oid = 1; /* chance of oid #1 being a novel is negligible */
3687 return read_tribute("Death", "Death Quotes", 0, buf, bufsz, death_oid);
3690 /* ---------- END TRIBUTE ----------- */