1 /* NetHack 3.6 files.c $NHDT-Date: 1455835581 2016/02/18 22:46:21 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.204 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
9 #include "wintty.h" /* more() */
12 #if (!defined(MAC) && !defined(O_WRONLY) && !defined(AZTEC_C)) \
18 #ifdef _MSC_VER /* MSC 6.0 defines errno quite differently */
34 #ifdef ZLIB_COMP /* RLC 09 Mar 1999: Support internal ZLIB */
36 #ifndef COMPRESS_EXTENSION
37 #define COMPRESS_EXTENSION ".gz"
41 #if defined(UNIX) && defined(QT_GRAPHICS)
42 #include <sys/types.h>
47 #if defined(UNIX) || defined(VMS) || !defined(NO_SIGNAL)
51 #if defined(MSDOS) || defined(OS2) || defined(TOS) || defined(WIN32)
58 #ifndef O_BINARY /* used for micros, no-op for others */
62 #ifdef PREFIXES_IN_USE
64 static char fqn_filename_buffer
[FQN_NUMBUF
][FQN_MAX_FILENAME
];
67 #if !defined(MFLOPPY) && !defined(VMS) && !defined(WIN32)
68 char bones
[] = "bonesnn.xxx";
69 char lock
[PL_NSIZ
+ 14] = "1lock"; /* long enough for uid+name+.99 */
72 char bones
[FILENAME
]; /* pathname of bones files */
73 char lock
[FILENAME
]; /* pathname of level files */
76 char bones
[] = "bonesnn.xxx;1";
77 char lock
[PL_NSIZ
+ 17] = "1lock"; /* long enough for _uid+name+.99;1 */
80 char bones
[] = "bonesnn.xxx";
81 char lock
[PL_NSIZ
+ 25]; /* long enough for username+-+name+.99 */
85 #if defined(UNIX) || defined(__BEOS__)
86 #define SAVESIZE (PL_NSIZ + 13) /* save/99999player.e */
89 #define SAVESIZE (PL_NSIZ + 22) /* [.save]<uid>player.e;1 */
92 #define SAVESIZE (PL_NSIZ + 40) /* username-player.NetHack-saved-game */
94 #define SAVESIZE FILENAME /* from macconf.h or pcconf.h */
99 #if !defined(SAVE_EXTENSION)
101 #define SAVE_EXTENSION ".sav"
104 #define SAVE_EXTENSION ".NetHack-saved-game"
108 char SAVEF
[SAVESIZE
]; /* holds relative path of save file from playground */
110 char SAVEP
[SAVESIZE
]; /* holds path of directory for save file */
113 #ifdef HOLD_LOCKFILE_OPEN
114 struct level_ftrack
{
116 int fd
; /* file descriptor for level file */
117 int oflag
; /* open flags */
118 boolean nethack_thinks_it_is_open
; /* Does NetHack think it's open? */
123 #endif /*HOLD_LOCKFILE_OPEN*/
125 #define WIZKIT_MAX 128
126 static char wizkit
[WIZKIT_MAX
];
127 STATIC_DCL
FILE *NDECL(fopen_wizkit_file
);
128 STATIC_DCL
void FDECL(wizkit_addinv
, (struct obj
*));
131 extern char PATH
[]; /* see sys/amiga/amidos.c */
132 extern char bbs_id
[];
135 #include <proto/dos.h>
138 #include <libraries/dos.h>
139 extern void FDECL(amii_set_text_font
, (char *, int));
142 #if defined(WIN32) || defined(MSDOS)
145 #define Delay(a) msleep(a)
149 #define DeleteFile unlink
155 #define unlink macunlink
158 #if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) \
159 || defined(__MWERKS__)
160 #define PRAGMA_UNUSED
164 extern char *sounddir
;
167 extern int n_dgns
; /* from dungeon.c */
169 #if defined(UNIX) && defined(QT_GRAPHICS)
174 STATIC_PTR
int FDECL(CFDECLSPEC strcmp_wrap
, (const void *, const void *));
176 STATIC_DCL
char *FDECL(set_bonesfile_name
, (char *, d_level
*));
177 STATIC_DCL
char *NDECL(set_bonestemp_name
);
179 STATIC_DCL
void FDECL(redirect
, (const char *, const char *, FILE *,
182 #if defined(COMPRESS) || defined(ZLIB_COMP)
183 STATIC_DCL
void FDECL(docompress_file
, (const char *, BOOLEAN_P
));
185 #if defined(ZLIB_COMP)
186 STATIC_DCL boolean
FDECL(make_compressed_name
, (const char *, char *));
189 STATIC_DCL
char *FDECL(make_lockname
, (const char *, char *));
191 STATIC_DCL
FILE *FDECL(fopen_config_file
, (const char *, int));
192 STATIC_DCL
int FDECL(get_uchars
, (FILE *, char *, char *, uchar
*, BOOLEAN_P
,
194 int FDECL(parse_config_line
, (FILE *, char *, int));
195 STATIC_DCL
FILE *NDECL(fopen_sym_file
);
196 STATIC_DCL
void FDECL(set_symhandling
, (char *, int));
197 #ifdef NOCWD_ASSUMPTIONS
198 STATIC_DCL
void FDECL(adjust_prefix
, (char *, int));
201 STATIC_DCL boolean
FDECL(copy_bytes
, (int, int));
203 #ifdef HOLD_LOCKFILE_OPEN
204 STATIC_DCL
int FDECL(open_levelfile_exclusively
, (const char *, int, int));
211 * legal zero-terminated list of acceptable file name characters
212 * quotechar lead-in character used to quote illegal characters as
215 * callerbuf buffer to house result
216 * bufsz size of callerbuf
219 * The hex digits 0-9 and A-F are always part of the legal set due to
220 * their use in the encoding scheme, even if not explicitly included in
224 * The following call:
225 * (void)fname_encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
226 * '%', "This is a % test!", buf, 512);
227 * results in this encoding:
228 * "This%20is%20a%20%25%20test%21"
231 fname_encode(legal
, quotechar
, s
, callerbuf
, bufsz
)
239 static char hexdigits
[] = "0123456789ABCDEF";
246 /* Do we have room for one more character or encoding? */
247 if ((bufsz
- cnt
) <= 4)
250 if (*sp
== quotechar
) {
251 (void) sprintf(op
, "%c%02X", quotechar
, *sp
);
254 } else if ((index(legal
, *sp
) != 0) || (index(hexdigits
, *sp
) != 0)) {
259 (void) sprintf(op
, "%c%02X", quotechar
, *sp
);
272 * quotechar lead-in character used to quote illegal characters as
275 * callerbuf buffer to house result
276 * bufsz size of callerbuf
279 fname_decode(quotechar
, s
, callerbuf
, bufsz
)
285 int k
, calc
, cnt
= 0;
286 static char hexdigits
[] = "0123456789ABCDEF";
294 /* Do we have room for one more character? */
295 if ((bufsz
- cnt
) <= 2)
297 if (*sp
== quotechar
) {
299 for (k
= 0; k
< 16; ++k
)
300 if (*sp
== hexdigits
[k
])
303 return callerbuf
; /* impossible, so bail */
306 for (k
= 0; k
< 16; ++k
)
307 if (*sp
== hexdigits
[k
])
310 return callerbuf
; /* impossible, so bail */
324 #ifdef PREFIXES_IN_USE
325 #define UNUSED_if_not_PREFIXES_IN_USE /*empty*/
327 #define UNUSED_if_not_PREFIXES_IN_USE UNUSED
332 fqname(basenam
, whichprefix
, buffnum
)
334 int whichprefix UNUSED_if_not_PREFIXES_IN_USE
;
335 int buffnum UNUSED_if_not_PREFIXES_IN_USE
;
337 #ifndef PREFIXES_IN_USE
340 if (!basenam
|| whichprefix
< 0 || whichprefix
>= PREFIX_COUNT
)
342 if (!fqn_prefix
[whichprefix
])
344 if (buffnum
< 0 || buffnum
>= FQN_NUMBUF
) {
345 impossible("Invalid fqn_filename_buffer specified: %d", buffnum
);
348 if (strlen(fqn_prefix
[whichprefix
]) + strlen(basenam
)
349 >= FQN_MAX_FILENAME
) {
350 impossible("fqname too long: %s + %s", fqn_prefix
[whichprefix
],
352 return basenam
; /* XXX */
354 Strcpy(fqn_filename_buffer
[buffnum
], fqn_prefix
[whichprefix
]);
355 return strcat(fqn_filename_buffer
[buffnum
], basenam
);
360 validate_prefix_locations(reasonbuf
)
361 char *reasonbuf
; /* reasonbuf must be at least BUFSZ, supplied by caller */
363 #if defined(NOCWD_ASSUMPTIONS)
365 const char *filename
;
366 int prefcnt
, failcount
= 0;
367 char panicbuf1
[BUFSZ
], panicbuf2
[BUFSZ
];
373 #if defined(NOCWD_ASSUMPTIONS)
374 for (prefcnt
= 1; prefcnt
< PREFIX_COUNT
; prefcnt
++) {
375 /* don't test writing to configdir or datadir; they're readonly */
376 if (prefcnt
== SYSCONFPREFIX
|| prefcnt
== CONFIGPREFIX
377 || prefcnt
== DATAPREFIX
)
379 filename
= fqname("validate", prefcnt
, 3);
380 if ((fp
= fopen(filename
, "w"))) {
382 (void) unlink(filename
);
386 Strcat(reasonbuf
, ", ");
387 Strcat(reasonbuf
, fqn_prefix_names
[prefcnt
]);
389 /* the paniclog entry gets the value of errno as well */
390 Sprintf(panicbuf1
, "Invalid %s", fqn_prefix_names
[prefcnt
]);
391 #if defined(NHSTDC) && !defined(NOTSTDC)
392 if (!(details
= strerror(errno
)))
395 Sprintf(panicbuf2
, "\"%s\", (%d) %s", fqn_prefix
[prefcnt
], errno
,
397 paniclog(panicbuf1
, panicbuf2
);
408 /* fopen a file, with OS-dependent bells and whistles */
409 /* NOTE: a simpler version of this routine also exists in util/dlb_main.c */
411 fopen_datafile(filename
, mode
, prefix
)
412 const char *filename
, *mode
;
417 filename
= fqname(filename
, prefix
, prefix
== TROUBLEPREFIX
? 3 : 0);
418 fp
= fopen(filename
, mode
);
422 /* ---------- BEGIN LEVEL FILE HANDLING ----------- */
425 /* Set names for bones[] and lock[] */
430 Strcpy(levels
, permbones
);
431 Strcpy(bones
, permbones
);
433 append_slash(permbones
);
434 append_slash(levels
);
436 strncat(levels
, bbs_id
, PATHLEN
);
439 Strcat(bones
, "bonesnn.*");
440 Strcpy(lock
, levels
);
442 Strcat(lock
, alllevels
);
448 /* Construct a file name for a level-type file, which is of the form
449 * something.level (with any old level stripped off).
450 * This assumes there is space on the end of 'file' to append
451 * a two digit number. This is true for 'level'
452 * but be careful if you use it for other things -dgk
455 set_levelfile_name(file
, lev
)
461 tf
= rindex(file
, '.');
464 Sprintf(tf
, ".%d", lev
);
472 create_levelfile(lev
, errbuf
)
481 set_levelfile_name(lock
, lev
);
482 fq_lock
= fqname(lock
, LEVELPREFIX
, 0);
484 #if defined(MICRO) || defined(WIN32)
485 /* Use O_TRUNC to force the file to be shortened if it already
486 * exists and is currently longer.
488 #ifdef HOLD_LOCKFILE_OPEN
490 fd
= open_levelfile_exclusively(
491 fq_lock
, lev
, O_WRONLY
| O_CREAT
| O_TRUNC
| O_BINARY
);
494 fd
= open(fq_lock
, O_WRONLY
| O_CREAT
| O_TRUNC
| O_BINARY
, FCMASK
);
497 fd
= maccreat(fq_lock
, LEVL_TYPE
);
499 fd
= creat(fq_lock
, FCMASK
);
501 #endif /* MICRO || WIN32 */
504 level_info
[lev
].flags
|= LFILE_EXISTS
;
505 else if (errbuf
) /* failure explanation */
506 Sprintf(errbuf
, "Cannot create file \"%s\" for level %d (errno %d).",
513 open_levelfile(lev
, errbuf
)
522 set_levelfile_name(lock
, lev
);
523 fq_lock
= fqname(lock
, LEVELPREFIX
, 0);
525 /* If not currently accessible, swap it in. */
526 if (level_info
[lev
].where
!= ACTIVE
)
530 fd
= macopen(fq_lock
, O_RDONLY
| O_BINARY
, LEVL_TYPE
);
532 #ifdef HOLD_LOCKFILE_OPEN
534 fd
= open_levelfile_exclusively(fq_lock
, lev
, O_RDONLY
| O_BINARY
);
537 fd
= open(fq_lock
, O_RDONLY
| O_BINARY
, 0);
540 /* for failure, return an explanation that our caller can use;
541 settle for `lock' instead of `fq_lock' because the latter
542 might end up being too big for nethack's BUFSZ */
543 if (fd
< 0 && errbuf
)
544 Sprintf(errbuf
, "Cannot open file \"%s\" for level %d (errno %d).",
551 delete_levelfile(lev
)
555 * Level 0 might be created by port specific code that doesn't
556 * call create_levfile(), so always assume that it exists.
558 if (lev
== 0 || (level_info
[lev
].flags
& LFILE_EXISTS
)) {
559 set_levelfile_name(lock
, lev
);
560 #ifdef HOLD_LOCKFILE_OPEN
564 (void) unlink(fqname(lock
, LEVELPREFIX
, 0));
565 level_info
[lev
].flags
&= ~LFILE_EXISTS
;
572 #ifdef HANGUPHANDLING
573 if (program_state
.preserve_locks
)
576 #if !defined(PC_LOCKING) && defined(MFLOPPY) && !defined(AMIGA)
577 eraseall(levels
, alllevels
);
579 eraseall(permbones
, alllevels
);
585 (void) signal(SIGINT
, SIG_IGN
);
587 #if defined(UNIX) || defined(VMS)
588 sethanguphandler((void FDECL((*), (int) )) SIG_IGN
);
590 /* can't access maxledgerno() before dungeons are created -dlc */
591 for (x
= (n_dgns
? maxledgerno() : 0); x
>= 0; x
--)
592 delete_levelfile(x
); /* not all levels need be present */
594 #endif /* ?PC_LOCKING,&c */
597 #if defined(SELECTSAVED)
598 /* qsort comparison routine */
599 STATIC_OVL
int CFDECLSPEC
604 #if defined(UNIX) && defined(QT_GRAPHICS)
605 return strncasecmp(*(char **) p
, *(char **) q
, 16);
607 return strncmpi(*(char **) p
, *(char **) q
, 16);
612 #ifdef HOLD_LOCKFILE_OPEN
614 open_levelfile_exclusively(name
, lev
, oflag
)
623 if (lftrack
.fd
>= 0) {
624 /* check for compatible access */
625 if (lftrack
.oflag
== oflag
) {
627 reslt
= lseek(fd
, 0L, SEEK_SET
);
629 panic("open_levelfile_exclusively: lseek failed %d", errno
);
630 lftrack
.nethack_thinks_it_is_open
= TRUE
;
633 fd
= sopen(name
, oflag
, SH_DENYRW
, FCMASK
);
635 lftrack
.oflag
= oflag
;
636 lftrack
.nethack_thinks_it_is_open
= TRUE
;
639 fd
= sopen(name
, oflag
, SH_DENYRW
, FCMASK
);
641 lftrack
.oflag
= oflag
;
643 lftrack
.nethack_thinks_it_is_open
= TRUE
;
653 lftrack
.nethack_thinks_it_is_open
= FALSE
;
665 if (lftrack
.fd
== fd
) {
666 really_close(); /* close it, but reopen it to hold it */
667 fd
= open_levelfile(0, (char *) 0);
668 lftrack
.nethack_thinks_it_is_open
= FALSE
;
683 /* ---------- END LEVEL FILE HANDLING ----------- */
685 /* ---------- BEGIN BONES FILE HANDLING ----------- */
687 /* set up "file" to be file name for retrieving bones, and return a
688 * bonesid to be read/written in the bones file.
691 set_bonesfile_name(file
, lev
)
698 Sprintf(file
, "bon%c%s", dungeons
[lev
->dnum
].boneid
,
699 In_quest(lev
) ? urole
.filecode
: "0");
701 if ((sptr
= Is_special(lev
)) != 0)
702 Sprintf(dptr
, ".%c", sptr
->boneid
);
704 Sprintf(dptr
, ".%d", lev
->dlevel
);
711 /* set up temporary file name for writing bones, to avoid another game's
712 * trying to read from an uncompleted bones file. we want an uncontentious
713 * name, so use one in the namespace reserved for this game's level files.
714 * (we are not reading or writing level files while writing bones files, so
715 * the same array may be used instead of copying.)
722 tf
= rindex(lock
, '.');
733 create_bonesfile(lev
, bonesid
, errbuf
)
743 *bonesid
= set_bonesfile_name(bones
, lev
);
744 file
= set_bonestemp_name();
745 file
= fqname(file
, BONESPREFIX
, 0);
747 #if defined(MICRO) || defined(WIN32)
748 /* Use O_TRUNC to force the file to be shortened if it already
749 * exists and is currently longer.
751 fd
= open(file
, O_WRONLY
| O_CREAT
| O_TRUNC
| O_BINARY
, FCMASK
);
754 fd
= maccreat(file
, BONE_TYPE
);
756 fd
= creat(file
, FCMASK
);
759 if (fd
< 0 && errbuf
) /* failure explanation */
760 Sprintf(errbuf
, "Cannot create bones \"%s\", id %s (errno %d).", lock
,
763 #if defined(VMS) && !defined(SECURE)
765 Re-protect bones file with world:read+write+execute+delete access.
766 umask() doesn't seem very reliable; also, vaxcrtl won't let us set
767 delete access without write access, which is what's really wanted.
768 Can't simply create it with the desired protection because creat
769 ANDs the mask with the user's default protection, which usually
770 denies some or all access to world.
772 (void) chmod(file
, FCMASK
| 007); /* allow other users full access */
773 #endif /* VMS && !SECURE */
779 /* remove partial bonesfile in process of creation */
783 const char *tempname
;
785 tempname
= set_bonestemp_name();
786 tempname
= fqname(tempname
, BONESPREFIX
, 0);
787 (void) unlink(tempname
);
791 /* move completed bones file to proper name */
793 commit_bonesfile(lev
)
796 const char *fq_bones
, *tempname
;
799 (void) set_bonesfile_name(bones
, lev
);
800 fq_bones
= fqname(bones
, BONESPREFIX
, 0);
801 tempname
= set_bonestemp_name();
802 tempname
= fqname(tempname
, BONESPREFIX
, 1);
804 #if (defined(SYSV) && !defined(SVR4)) || defined(GENIX)
805 /* old SYSVs don't have rename. Some SVR3's may, but since they
806 * also have link/unlink, it doesn't matter. :-)
808 (void) unlink(fq_bones
);
809 ret
= link(tempname
, fq_bones
);
810 ret
+= unlink(tempname
);
812 ret
= rename(tempname
, fq_bones
);
814 if (wizard
&& ret
!= 0)
815 pline("couldn't rename %s to %s.", tempname
, fq_bones
);
819 open_bonesfile(lev
, bonesid
)
823 const char *fq_bones
;
826 *bonesid
= set_bonesfile_name(bones
, lev
);
827 fq_bones
= fqname(bones
, BONESPREFIX
, 0);
828 nh_uncompress(fq_bones
); /* no effect if nonexistent */
830 fd
= macopen(fq_bones
, O_RDONLY
| O_BINARY
, BONE_TYPE
);
832 fd
= open(fq_bones
, O_RDONLY
| O_BINARY
, 0);
838 delete_bonesfile(lev
)
841 (void) set_bonesfile_name(bones
, lev
);
842 return !(unlink(fqname(bones
, BONESPREFIX
, 0)) < 0);
845 /* assume we're compressing the recently read or created bonesfile, so the
846 * file name is already set properly */
850 nh_compress(fqname(bones
, BONESPREFIX
, 0));
853 /* ---------- END BONES FILE HANDLING ----------- */
855 /* ---------- BEGIN SAVE FILE HANDLING ----------- */
857 /* set savefile name in OS-dependent manner from pre-existing plname,
858 * avoiding troublesome characters */
860 set_savefile_name(regularize_it
)
861 boolean regularize_it
;
864 Sprintf(SAVEF
, "[.save]%d%s", getuid(), plname
);
866 regularize(SAVEF
+ 7);
870 Strcpy(SAVEF
, SAVEP
);
872 strncat(SAVEF
, bbs_id
, PATHLEN
);
875 int i
= strlen(SAVEP
);
877 /* plname has to share space with SAVEP and ".sav" */
878 (void) strncat(SAVEF
, plname
, FILENAME
- i
- 4);
880 (void) strncat(SAVEF
, plname
, 8);
883 regularize(SAVEF
+ i
);
885 Strcat(SAVEF
, SAVE_EXTENSION
);
889 static const char okchars
[] =
890 "*ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.";
891 const char *legal
= okchars
;
892 char fnamebuf
[BUFSZ
], encodedfnamebuf
[BUFSZ
];
894 /* Obtain the name of the logged on user and incorporate
895 * it into the name. */
896 Sprintf(fnamebuf
, "%s-%s", get_username(0), plname
);
898 ++legal
; /* skip '*' wildcard character */
899 (void) fname_encode(legal
, '%', fnamebuf
, encodedfnamebuf
, BUFSZ
);
900 Sprintf(SAVEF
, "%s%s", encodedfnamebuf
, SAVE_EXTENSION
);
902 #else /* not VMS or MICRO or WIN32 */
903 Sprintf(SAVEF
, "save/%d%s", (int) getuid(), plname
);
905 regularize(SAVEF
+ 5); /* avoid . or / in name */
913 save_savefile_name(fd
)
916 (void) write(fd
, (genericptr_t
) SAVEF
, sizeof(SAVEF
));
921 /* change pre-existing savefile name to indicate an error savefile */
927 char *semi_colon
= rindex(SAVEF
, ';');
932 Strcat(SAVEF
, ".e;1");
943 /* create save file, overwriting one if it already exists */
950 fq_save
= fqname(SAVEF
, SAVEPREFIX
, 0);
951 #if defined(MICRO) || defined(WIN32)
952 fd
= open(fq_save
, O_WRONLY
| O_BINARY
| O_CREAT
| O_TRUNC
, FCMASK
);
955 fd
= maccreat(fq_save
, SAVE_TYPE
);
957 fd
= creat(fq_save
, FCMASK
);
959 #if defined(VMS) && !defined(SECURE)
961 Make sure the save file is owned by the current process. That's
962 the default for non-privileged users, but for priv'd users the
963 file will be owned by the directory's owner instead of the user.
966 (void) chown(fq_save
, getuid(), getgid());
967 #define getuid() vms_getuid()
968 #endif /* VMS && !SECURE */
974 /* open savefile for reading */
981 fq_save
= fqname(SAVEF
, SAVEPREFIX
, 0);
983 fd
= macopen(fq_save
, O_RDONLY
| O_BINARY
, SAVE_TYPE
);
985 fd
= open(fq_save
, O_RDONLY
| O_BINARY
, 0);
990 /* delete savefile */
994 (void) unlink(fqname(SAVEF
, SAVEPREFIX
, 0));
995 return 0; /* for restore_saved_game() (ex-xxxmain.c) test */
998 /* try to open up a save file and prepare to restore it */
1000 restore_saved_game()
1002 const char *fq_save
;
1006 set_savefile_name(TRUE
);
1008 if (!saveDiskPrompt(1))
1010 #endif /* MFLOPPY */
1011 fq_save
= fqname(SAVEF
, SAVEPREFIX
, 0);
1013 nh_uncompress(fq_save
);
1014 if ((fd
= open_savefile()) < 0)
1017 if (validate(fd
, fq_save
) != 0) {
1018 (void) nhclose(fd
), fd
= -1;
1019 (void) delete_savefile();
1024 #if defined(SELECTSAVED)
1026 plname_from_file(filename
)
1027 const char *filename
;
1032 Strcpy(SAVEF
, filename
);
1033 #ifdef COMPRESS_EXTENSION
1034 SAVEF
[strlen(SAVEF
) - strlen(COMPRESS_EXTENSION
)] = '\0';
1036 nh_uncompress(SAVEF
);
1037 if ((fd
= open_savefile()) >= 0) {
1038 if (validate(fd
, filename
) == 0) {
1039 char tplname
[PL_NSIZ
];
1040 get_plname_from_file(fd
, tplname
);
1041 result
= dupstr(tplname
);
1049 /* --------- obsolete - used to be ifndef STORE_PLNAME_IN_FILE ----*/
1050 #if defined(UNIX) && defined(QT_GRAPHICS)
1051 /* Name not stored in save file, so we have to extract it from
1052 the filename, which loses information
1053 (eg. "/", "_", and "." characters are lost. */
1056 char name
[64]; /* more than PL_NSIZ */
1057 #ifdef COMPRESS_EXTENSION
1058 #define EXTSTR COMPRESS_EXTENSION
1063 if ( sscanf( filename
, "%*[^/]/%d%63[^.]" EXTSTR
, &uid
, name
) == 2 ) {
1065 /* "_" most likely means " ", which certainly looks nicer */
1066 for (k
=0; name
[k
]; k
++)
1067 if ( name
[k
] == '_' )
1069 return dupstr(name
);
1071 #endif /* UNIX && QT_GRAPHICS */
1075 /* --------- end of obsolete code ----*/
1076 #endif /* 0 - WAS STORE_PLNAME_IN_FILE*/
1078 #endif /* defined(SELECTSAVED) */
1083 #if defined(SELECTSAVED)
1089 const char *fq_save
;
1091 Strcpy(plname
, "*");
1092 set_savefile_name(FALSE
);
1093 #if defined(ZLIB_COMP)
1094 Strcat(SAVEF
, COMPRESS_EXTENSION
);
1096 fq_save
= fqname(SAVEF
, SAVEPREFIX
, 0);
1099 foundfile
= foundfile_buffer();
1100 if (findfirst((char *) fq_save
)) {
1103 } while (findnext());
1106 result
= (char **) alloc((n
+ 1) * sizeof(char *)); /* at most */
1107 (void) memset((genericptr_t
) result
, 0, (n
+ 1) * sizeof(char *));
1108 if (findfirst((char *) fq_save
)) {
1112 r
= plname_from_file(foundfile
);
1116 } while (findnext());
1121 #if defined(UNIX) && defined(QT_GRAPHICS)
1122 /* posixly correct version */
1123 int myuid
= getuid();
1126 if ((dir
= opendir(fqname("save", SAVEPREFIX
, 0)))) {
1127 for (n
= 0; readdir(dir
); n
++)
1133 if (!(dir
= opendir(fqname("save", SAVEPREFIX
, 0))))
1135 result
= (char **) alloc((n
+ 1) * sizeof(char *)); /* at most */
1136 (void) memset((genericptr_t
) result
, 0, (n
+ 1) * sizeof(char *));
1137 for (i
= 0, j
= 0; i
< n
; i
++) {
1139 char name
[64]; /* more than PL_NSIZ */
1140 struct dirent
*entry
= readdir(dir
);
1144 if (sscanf(entry
->d_name
, "%d%63s", &uid
, name
) == 2) {
1146 char filename
[BUFSZ
];
1149 Sprintf(filename
, "save/%d%s", uid
, name
);
1150 r
= plname_from_file(filename
);
1161 Strcpy(plname
, "*");
1162 set_savefile_name(FALSE
);
1163 j
= vms_get_saved_games(SAVEF
, &result
);
1168 qsort(result
, j
, sizeof (char *), strcmp_wrap
);
1171 } else if (result
) { /* could happen if save files are obsolete */
1172 free_saved_games(result
);
1174 #endif /* SELECTSAVED */
1179 free_saved_games(saved
)
1186 free((genericptr_t
) saved
[i
++]);
1187 free((genericptr_t
) saved
);
1191 /* ---------- END SAVE FILE HANDLING ----------- */
1193 /* ---------- BEGIN FILE COMPRESSION HANDLING ----------- */
1198 redirect(filename
, mode
, stream
, uncomp
)
1199 const char *filename
, *mode
;
1203 if (freopen(filename
, mode
, stream
) == (FILE *) 0) {
1204 (void) fprintf(stderr
, "freopen of %s for %scompress failed\n",
1205 filename
, uncomp
? "un" : "");
1206 terminate(EXIT_FAILURE
);
1211 * using system() is simpler, but opens up security holes and causes
1212 * problems on at least Interactive UNIX 3.0.1 (SVR3.2), where any
1213 * setuid is renounced by /bin/sh, so the files cannot be accessed.
1215 * cf. child() in unixunix.c.
1218 docompress_file(filename
, uncomp
)
1219 const char *filename
;
1224 const char *args
[10];
1225 #ifdef COMPRESS_OPTIONS
1231 boolean istty
= !strncmpi(windowprocs
.name
, "tty", 3);
1234 Strcpy(cfn
, filename
);
1235 #ifdef COMPRESS_EXTENSION
1236 Strcat(cfn
, COMPRESS_EXTENSION
);
1238 /* when compressing, we know the file exists */
1240 if ((cf
= fopen(cfn
, RDBMODE
)) == (FILE *) 0)
1247 args
[++i
] = "-d"; /* uncompress */
1248 #ifdef COMPRESS_OPTIONS
1250 /* we can't guarantee there's only one additional option, sigh */
1252 boolean inword
= FALSE
;
1254 Strcpy(opts
, COMPRESS_OPTIONS
);
1257 if ((*opt
== ' ') || (*opt
== '\t')) {
1262 } else if (!inword
) {
1270 args
[++i
] = (char *) 0;
1273 /* If we don't do this and we are right after a y/n question *and*
1274 * there is an error message from the compression, the 'y' or 'n' can
1275 * end up being displayed after the error message.
1281 if (f
== 0) { /* child */
1283 /* any error messages from the compression must come out after
1284 * the first line, because the more() to let the user read
1285 * them will have to clear the first line. This should be
1286 * invisible if there are no error messages.
1291 /* run compressor without privileges, in case other programs
1292 * have surprises along the line of gzip once taking filenames
1295 /* assume all compressors will compress stdin to stdout
1296 * without explicit filenames. this is true of at least
1297 * compress and gzip, those mentioned in config.h.
1300 redirect(cfn
, RDBMODE
, stdin
, uncomp
);
1301 redirect(filename
, WRBMODE
, stdout
, uncomp
);
1303 redirect(filename
, RDBMODE
, stdin
, uncomp
);
1304 redirect(cfn
, WRBMODE
, stdout
, uncomp
);
1306 (void) setgid(getgid());
1307 (void) setuid(getuid());
1308 (void) execv(args
[0], (char *const *) args
);
1310 (void) fprintf(stderr
, "Exec to %scompress %s failed.\n",
1311 uncomp
? "un" : "", filename
);
1312 terminate(EXIT_FAILURE
);
1313 } else if (f
== -1) {
1315 pline("Fork to %scompress %s failed.", uncomp
? "un" : "", filename
);
1319 (void) signal(SIGINT
, SIG_IGN
);
1320 (void) signal(SIGQUIT
, SIG_IGN
);
1321 (void) wait((int *) &i
);
1322 (void) signal(SIGINT
, (SIG_RET_TYPE
) done1
);
1324 (void) signal(SIGQUIT
, SIG_DFL
);
1326 /* I don't think we can really cope with external compression
1327 * without signals, so we'll declare that compress failed and
1328 * go on. (We could do a better job by forcing off external
1329 * compression if there are no signals, but we want this for
1330 * testing with FailSafeC
1335 /* (un)compress succeeded: remove file left behind */
1339 (void) unlink(filename
);
1341 /* (un)compress failed; remove the new, bad file */
1343 raw_printf("Unable to uncompress %s", filename
);
1344 (void) unlink(filename
);
1346 /* no message needed for compress case; life will go on */
1350 /* Give them a chance to read any error messages from the
1351 * compression--these would go to stdout or stderr and would get
1352 * overwritten only in tty mode. It's still ugly, since the
1353 * messages are being written on top of the screen, but at least
1354 * the user can read them.
1356 if (istty
&& iflags
.window_inited
) {
1357 clear_nhwindow(WIN_MESSAGE
);
1359 /* No way to know if this is feasible */
1365 #endif /* COMPRESS */
1367 #if defined(COMPRESS) || defined(ZLIB_COMP)
1368 #define UNUSED_if_not_COMPRESS /*empty*/
1370 #define UNUSED_if_not_COMPRESS UNUSED
1375 nh_compress(filename
)
1376 const char *filename UNUSED_if_not_COMPRESS
;
1378 #if !defined(COMPRESS) && !defined(ZLIB_COMP)
1379 #ifdef PRAGMA_UNUSED
1380 #pragma unused(filename)
1383 docompress_file(filename
, FALSE
);
1387 /* uncompress file if it exists */
1389 nh_uncompress(filename
)
1390 const char *filename UNUSED_if_not_COMPRESS
;
1392 #if !defined(COMPRESS) && !defined(ZLIB_COMP)
1393 #ifdef PRAGMA_UNUSED
1394 #pragma unused(filename)
1397 docompress_file(filename
, TRUE
);
1401 #ifdef ZLIB_COMP /* RLC 09 Mar 1999: Support internal ZLIB */
1403 make_compressed_name(filename
, cfn
)
1404 const char *filename
;
1407 #ifndef SHORT_FILENAMES
1408 /* Assume free-form filename with no 8.3 restrictions */
1409 strcpy(cfn
, filename
);
1410 strcat(cfn
, COMPRESS_EXTENSION
);
1413 #ifdef SAVE_EXTENSION
1414 char *bp
= (char *) 0;
1416 strcpy(cfn
, filename
);
1417 if ((bp
= strstri(cfn
, SAVE_EXTENSION
))) {
1418 strsubst(bp
, SAVE_EXTENSION
, ".saz");
1421 /* find last occurrence of bon */
1423 while (bp
-- > cfn
) {
1424 if (strstri(bp
, "bon")) {
1425 strsubst(bp
, "bon", "boz");
1430 #endif /* SAVE_EXTENSION */
1432 #endif /* SHORT_FILENAMES */
1436 docompress_file(filename
, uncomp
)
1437 const char *filename
;
1440 gzFile compressedfile
;
1441 FILE *uncompressedfile
;
1446 if (!make_compressed_name(filename
, cfn
))
1450 /* Open the input and output files */
1451 /* Note that gzopen takes "wb" as its mode, even on systems where
1452 fopen takes "r" and "w" */
1454 uncompressedfile
= fopen(filename
, RDBMODE
);
1455 if (!uncompressedfile
) {
1456 pline("Error in zlib docompress_file %s", filename
);
1459 compressedfile
= gzopen(cfn
, "wb");
1460 if (compressedfile
== NULL
) {
1462 pline("zlib failed to allocate memory");
1464 panic("Error in docompress_file %d", errno
);
1466 fclose(uncompressedfile
);
1470 /* Copy from the uncompressed to the compressed file */
1473 len
= fread(buf
, 1, sizeof(buf
), uncompressedfile
);
1474 if (ferror(uncompressedfile
)) {
1475 pline("Failure reading uncompressed file");
1476 pline("Can't compress %s.", filename
);
1477 fclose(uncompressedfile
);
1478 gzclose(compressedfile
);
1483 break; /* End of file */
1485 len2
= gzwrite(compressedfile
, buf
, len
);
1487 pline("Failure writing compressed file");
1488 pline("Can't compress %s.", filename
);
1489 fclose(uncompressedfile
);
1490 gzclose(compressedfile
);
1496 fclose(uncompressedfile
);
1497 gzclose(compressedfile
);
1499 /* Delete the file left behind */
1501 (void) unlink(filename
);
1503 } else { /* uncomp */
1505 /* Open the input and output files */
1506 /* Note that gzopen takes "rb" as its mode, even on systems where
1507 fopen takes "r" and "w" */
1509 compressedfile
= gzopen(cfn
, "rb");
1510 if (compressedfile
== NULL
) {
1512 pline("zlib failed to allocate memory");
1513 } else if (errno
!= ENOENT
) {
1514 panic("Error in zlib docompress_file %s, %d", filename
,
1519 uncompressedfile
= fopen(filename
, WRBMODE
);
1520 if (!uncompressedfile
) {
1521 pline("Error in zlib docompress file uncompress %s", filename
);
1522 gzclose(compressedfile
);
1526 /* Copy from the compressed to the uncompressed file */
1529 len
= gzread(compressedfile
, buf
, sizeof(buf
));
1530 if (len
== (unsigned) -1) {
1531 pline("Failure reading compressed file");
1532 pline("Can't uncompress %s.", filename
);
1533 fclose(uncompressedfile
);
1534 gzclose(compressedfile
);
1535 (void) unlink(filename
);
1539 break; /* End of file */
1541 fwrite(buf
, 1, len
, uncompressedfile
);
1542 if (ferror(uncompressedfile
)) {
1543 pline("Failure writing uncompressed file");
1544 pline("Can't uncompress %s.", filename
);
1545 fclose(uncompressedfile
);
1546 gzclose(compressedfile
);
1547 (void) unlink(filename
);
1552 fclose(uncompressedfile
);
1553 gzclose(compressedfile
);
1555 /* Delete the file left behind */
1559 #endif /* RLC 09 Mar 1999: End ZLIB patch */
1561 /* ---------- END FILE COMPRESSION HANDLING ----------- */
1563 /* ---------- BEGIN FILE LOCKING HANDLING ----------- */
1565 static int nesting
= 0;
1567 #if defined(NO_FILE_LINKS) || defined(USE_FCNTL) /* implies UNIX */
1568 static int lockfd
; /* for lock_file() to pass to unlock_file() */
1571 struct flock sflock
; /* for unlocking, same as above */
1574 #define HUP if (!program_state.done_hup)
1578 make_lockname(filename
, lockname
)
1579 const char *filename
;
1582 #if defined(UNIX) || defined(VMS) || defined(AMIGA) || defined(WIN32) \
1584 #ifdef NO_FILE_LINKS
1585 Strcpy(lockname
, LOCKDIR
);
1586 Strcat(lockname
, "/");
1587 Strcat(lockname
, filename
);
1589 Strcpy(lockname
, filename
);
1593 char *semi_colon
= rindex(lockname
, ';');
1597 Strcat(lockname
, ".lock;1");
1599 Strcat(lockname
, "_lock");
1602 #else /* !(UNIX || VMS || AMIGA || WIN32 || MSDOS) */
1603 #ifdef PRAGMA_UNUSED
1604 #pragma unused(filename)
1610 #endif /* !USE_FCNTL */
1614 lock_file(filename
, whichprefix
, retryct
)
1615 const char *filename
;
1619 #if defined(PRAGMA_UNUSED) && !(defined(UNIX) || defined(VMS)) \
1620 && !(defined(AMIGA) || defined(WIN32) || defined(MSDOS))
1621 #pragma unused(retryct)
1624 char locknambuf
[BUFSZ
];
1625 const char *lockname
;
1630 impossible("TRIED TO NEST LOCKS");
1635 lockname
= make_lockname(filename
, locknambuf
);
1636 #ifndef NO_FILE_LINKS /* LOCKDIR should be subsumed by LOCKPREFIX */
1637 lockname
= fqname(lockname
, LOCKPREFIX
, 2);
1640 filename
= fqname(filename
, whichprefix
, 0);
1642 lockfd
= open(filename
, O_RDWR
);
1644 HUP
raw_printf("Cannot open file %s. This is a program bug.",
1647 sflock
.l_type
= F_WRLCK
;
1648 sflock
.l_whence
= SEEK_SET
;
1653 #if defined(UNIX) || defined(VMS)
1655 while (fcntl(lockfd
, F_SETLK
, &sflock
) == -1) {
1657 #ifdef NO_FILE_LINKS
1658 while ((lockfd
= open(lockname
, O_RDWR
| O_CREAT
| O_EXCL
, 0666)) == -1) {
1660 while (link(filename
, lockname
) == -1) {
1667 "Waiting for release of fcntl lock on %s. (%d retries left).",
1671 HUP(void) raw_print("I give up. Sorry.");
1672 HUP
raw_printf("Some other process has an unnatural grip on %s.",
1678 register int errnosv
= errno
;
1680 switch (errnosv
) { /* George Barbanis */
1684 "Waiting for access to %s. (%d retries left).", filename
,
1686 #if defined(SYSV) || defined(ULTRIX) || defined(VMS)
1691 HUP(void) raw_print("I give up. Sorry.");
1692 HUP
raw_printf("Perhaps there is an old %s around?",
1700 HUP
raw_printf("Can't find file %s to lock!", filename
);
1704 HUP
raw_printf("No write permission to lock %s!", filename
);
1707 #ifdef VMS /* c__translate(vmsfiles.c) */
1709 /* could be misleading, but usually right */
1710 HUP
raw_printf("Can't lock %s due to directory protection.",
1716 /* take a wild guess at the underlying cause */
1717 HUP
perror(lockname
);
1718 HUP
raw_printf("Cannot lock %s.", filename
);
1720 "(Perhaps you are running NetHack from inside the distribution package?).");
1724 HUP
perror(lockname
);
1725 HUP
raw_printf("Cannot lock %s for unknown reason (%d).",
1730 #endif /* USE_FCNTL */
1732 #endif /* UNIX || VMS */
1734 #if (defined(AMIGA) || defined(WIN32) || defined(MSDOS)) \
1735 && !defined(USE_FCNTL)
1737 #define OPENFAILURE(fd) (!fd)
1740 #define OPENFAILURE(fd) (fd < 0)
1743 while (--retryct
&& OPENFAILURE(lockptr
)) {
1744 #if defined(WIN32) && !defined(WIN_CE)
1745 lockptr
= sopen(lockname
, O_RDWR
| O_CREAT
, SH_DENYRW
, S_IWRITE
);
1747 (void) DeleteFile(lockname
); /* in case dead process was here first */
1749 lockptr
= Open(lockname
, MODE_NEWFILE
);
1751 lockptr
= open(lockname
, O_RDWR
| O_CREAT
| O_EXCL
, S_IWRITE
);
1754 if (OPENFAILURE(lockptr
)) {
1755 raw_printf("Waiting for access to %s. (%d retries left).",
1761 raw_printf("I give up. Sorry.");
1765 #endif /* AMIGA || WIN32 || MSDOS */
1769 #ifdef VMS /* for unlock_file, use the unlink() routine in vmsunix.c */
1773 #define unlink(foo) vms_unlink(foo)
1776 /* unlock file, which must be currently locked by lock_file */
1778 unlock_file(filename
)
1779 const char *filename
;
1782 char locknambuf
[BUFSZ
];
1783 const char *lockname
;
1788 sflock
.l_type
= F_UNLCK
;
1789 if (fcntl(lockfd
, F_SETLK
, &sflock
) == -1) {
1790 HUP
raw_printf("Can't remove fcntl lock on %s.", filename
);
1791 (void) close(lockfd
);
1794 lockname
= make_lockname(filename
, locknambuf
);
1795 #ifndef NO_FILE_LINKS /* LOCKDIR should be subsumed by LOCKPREFIX */
1796 lockname
= fqname(lockname
, LOCKPREFIX
, 2);
1799 #if defined(UNIX) || defined(VMS)
1800 if (unlink(lockname
) < 0)
1801 HUP
raw_printf("Can't unlink %s.", lockname
);
1802 #ifdef NO_FILE_LINKS
1803 (void) nhclose(lockfd
);
1806 #endif /* UNIX || VMS */
1808 #if defined(AMIGA) || defined(WIN32) || defined(MSDOS)
1811 DeleteFile(lockname
);
1813 #endif /* AMIGA || WIN32 || MSDOS */
1814 #endif /* USE_FCNTL */
1820 /* ---------- END FILE LOCKING HANDLING ----------- */
1822 /* ---------- BEGIN CONFIG FILE HANDLING ----------- */
1824 const char *configfile
=
1828 #if defined(MAC) || defined(__BEOS__)
1831 #if defined(MSDOS) || defined(WIN32)
1839 /* used for messaging */
1840 char lastconfigfile
[BUFSZ
];
1843 /* conflict with speed-dial under windows
1844 * for XXX.cnf file so support of NetHack.cnf
1845 * is for backward compatibility only.
1846 * Preferred name (and first tried) is now defaults.nh but
1847 * the game will try the old name if there
1848 * is no defaults.nh.
1850 const char *backward_compat_configfile
= "nethack.cnf";
1854 #define fopenp fopen
1858 fopen_config_file(filename
, src
)
1859 const char *filename
;
1863 #if defined(UNIX) || defined(VMS)
1864 char tmp_config
[BUFSZ
];
1868 /* If src != SET_IN_SYS, "filename" is an environment variable, so it
1869 * should hang around. If set, it is expected to be a full path name
1873 if ((src
!= SET_IN_SYS
) && access(filename
, 4) == -1) {
1874 /* 4 is R_OK on newer systems */
1875 /* nasty sneaky attempt to read file through
1876 * NetHack's setuid permissions -- this is the only
1877 * place a file name may be wholly under the player's
1878 * control (but SYSCF_FILE is not under the player's
1879 * control so it's OK).
1881 raw_printf("Access to %s denied (%d).", filename
, errno
);
1883 /* fall through to standard names */
1886 #ifdef PREFIXES_IN_USE
1887 if (src
== SET_IN_SYS
) {
1888 (void) strncpy(lastconfigfile
, fqname(filename
, SYSCONFPREFIX
, 0),
1892 /* always honor sysconf first before anything else */
1893 (void) strncpy(lastconfigfile
, filename
, BUFSZ
- 1);
1894 lastconfigfile
[BUFSZ
- 1] = '\0';
1895 if ((fp
= fopenp(lastconfigfile
, "r")) != (FILE *) 0)
1897 if ((fp
= fopenp(filename
, "r")) != (FILE *) 0) {
1899 #if defined(UNIX) || defined(VMS)
1901 /* access() above probably caught most problems for UNIX */
1902 raw_printf("Couldn't open requested config file %s (%d).",
1905 /* fall through to standard names */
1910 #if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32)
1911 if ((fp
= fopenp(fqname(configfile
, CONFIGPREFIX
, 0), "r")) != (FILE *) 0)
1913 if ((fp
= fopenp(configfile
, "r")) != (FILE *) 0)
1916 if ((fp
= fopenp(fqname(backward_compat_configfile
, CONFIGPREFIX
, 0),
1917 "r")) != (FILE *) 0)
1919 if ((fp
= fopenp(backward_compat_configfile
, "r")) != (FILE *) 0)
1923 /* constructed full path names don't need fqname() */
1925 (void) strncpy(lastconfigfile
, fqname("nethackini", CONFIGPREFIX
, 0),
1927 lastconfigfile
[BUFSZ
- 1] = '\0';
1928 if ((fp
= fopenp(lastconfigfile
, "r")) != (FILE *) 0) {
1931 (void) strncpy(lastconfigfile
, "sys$login:nethack.ini", BUFSZ
- 1);
1932 lastconfigfile
[BUFSZ
- 1] = '\0';
1933 if ((fp
= fopenp(lastconfigfile
, "r")) != (FILE *) 0) {
1937 envp
= nh_getenv("HOME");
1939 Strcpy(tmp_config
, "NetHack.cnf");
1941 Sprintf(tmp_config
, "%s%s", envp
, "NetHack.cnf");
1943 (void) strncpy(lastconfigfile
, tmp_config
, BUFSZ
- 1);
1944 lastconfigfile
[BUFSZ
- 1] = '\0';
1945 if ((fp
= fopenp(tmp_config
, "r")) != (FILE *) 0)
1947 #else /* should be only UNIX left */
1948 envp
= nh_getenv("HOME");
1950 Strcpy(tmp_config
, ".nethackrc");
1952 Sprintf(tmp_config
, "%s/%s", envp
, ".nethackrc");
1954 (void) strncpy(lastconfigfile
, tmp_config
, BUFSZ
- 1);
1955 lastconfigfile
[BUFSZ
- 1] = '\0';
1956 if ((fp
= fopenp(lastconfigfile
, "r")) != (FILE *) 0)
1958 #if defined(__APPLE__)
1959 /* try an alternative */
1961 Sprintf(tmp_config
, "%s/%s", envp
,
1962 "Library/Preferences/NetHack Defaults");
1963 (void) strncpy(lastconfigfile
, tmp_config
, BUFSZ
- 1);
1964 lastconfigfile
[BUFSZ
- 1] = '\0';
1965 if ((fp
= fopenp(lastconfigfile
, "r")) != (FILE *) 0)
1967 Sprintf(tmp_config
, "%s/%s", envp
,
1968 "Library/Preferences/NetHack Defaults.txt");
1969 (void) strncpy(lastconfigfile
, tmp_config
, BUFSZ
- 1);
1970 lastconfigfile
[BUFSZ
- 1] = '\0';
1971 if ((fp
= fopenp(lastconfigfile
, "r")) != (FILE *) 0)
1975 if (errno
!= ENOENT
) {
1976 const char *details
;
1978 /* e.g., problems when setuid NetHack can't search home
1979 * directory restricted to user */
1981 #if defined(NHSTDC) && !defined(NOTSTDC)
1982 if ((details
= strerror(errno
)) == 0)
1985 raw_printf("Couldn't open default config file %s %s(%d).",
1986 lastconfigfile
, details
, errno
);
1995 * Retrieve a list of integers from a file into a uchar array.
1997 * NOTE: zeros are inserted unless modlist is TRUE, in which case the list
1998 * location is unchanged. Callers must handle zeros if modlist is FALSE.
2001 get_uchars(fp
, buf
, bufp
, list
, modlist
, size
, name
)
2002 FILE *fp
; /* input file pointer */
2003 char *buf
; /* read buffer, must be of size BUFSZ */
2004 char *bufp
; /* current pointer */
2005 uchar
*list
; /* return list */
2006 boolean modlist
; /* TRUE: list is being modified in place */
2007 int size
; /* return list size */
2008 const char *name
; /* name of option for error message */
2010 unsigned int num
= 0;
2012 boolean havenum
= FALSE
;
2021 /* if modifying in place, don't insert zeros */
2022 if (num
|| !modlist
)
2028 if (count
== size
|| !*bufp
)
2044 num
= num
* 10 + (*bufp
- '0');
2049 if (fp
== (FILE *) 0)
2052 if (!fgets(buf
, BUFSZ
, fp
))
2054 } while (buf
[0] == '#');
2060 raw_printf("Syntax error in %s", name
);
2068 #ifdef NOCWD_ASSUMPTIONS
2070 adjust_prefix(bufp
, prefixid
)
2078 /* Backward compatibility, ignore trailing ;n */
2079 if ((ptr
= index(bufp
, ';')) != 0)
2081 if (strlen(bufp
) > 0) {
2082 fqn_prefix
[prefixid
] = (char *) alloc(strlen(bufp
) + 2);
2083 Strcpy(fqn_prefix
[prefixid
], bufp
);
2084 append_slash(fqn_prefix
[prefixid
]);
2089 #define match_varname(INP, NAM, LEN) match_optname(INP, NAM, LEN, TRUE)
2092 parse_config_line(fp
, origbuf
, src
)
2097 #if defined(MICRO) && !defined(NOCWD_ASSUMPTIONS)
2098 static boolean ramdisk_specified
= FALSE
;
2103 char *bufp
, *altp
, buf
[4 * BUFSZ
];
2104 uchar translate
[MAXPCHARS
];
2107 /* convert any tab to space, condense consecutive spaces into one,
2108 remove leading and trailing spaces (exception: if there is nothing
2109 but spaces, one of them will be kept even though it leads/trails) */
2110 mungspaces(strcpy(buf
, origbuf
));
2111 /* lines beginning with '#' are comments; accept empty lines too */
2112 if (!*buf
|| *buf
== '#' || !strcmp(buf
, " "))
2115 /* find the '=' or ':' */
2116 bufp
= index(buf
, '=');
2117 altp
= index(buf
, ':');
2118 if (!bufp
|| (altp
&& altp
< bufp
))
2122 /* skip past '=', then space between it and value, if any */
2127 /* Go through possible variables */
2128 /* some of these (at least LEVELS and SAVE) should now set the
2129 * appropriate fqn_prefix[] rather than specialized variables
2131 if (match_varname(buf
, "OPTIONS", 4)) {
2132 /* hack: un-mungspaces to allow consecutive spaces in
2133 general options until we verify that this is unnecessary;
2134 '=' or ':' is guaranteed to be present */
2135 bufp
= index(origbuf
, '=');
2136 altp
= index(origbuf
, ':');
2137 if (!bufp
|| (altp
&& altp
< bufp
))
2139 ++bufp
; /* skip '='; parseoptions() handles spaces */
2141 parseoptions(bufp
, TRUE
, TRUE
);
2142 } else if (match_varname(buf
, "AUTOPICKUP_EXCEPTION", 5)) {
2143 add_autopickup_exception(bufp
);
2144 } else if (match_varname(buf
, "MSGTYPE", 7)) {
2145 (void) msgtype_parse_add(bufp
);
2146 #ifdef NOCWD_ASSUMPTIONS
2147 } else if (match_varname(buf
, "HACKDIR", 4)) {
2148 adjust_prefix(bufp
, HACKPREFIX
);
2149 } else if (match_varname(buf
, "LEVELDIR", 4)
2150 || match_varname(buf
, "LEVELS", 4)) {
2151 adjust_prefix(bufp
, LEVELPREFIX
);
2152 } else if (match_varname(buf
, "SAVEDIR", 4)) {
2153 adjust_prefix(bufp
, SAVEPREFIX
);
2154 } else if (match_varname(buf
, "BONESDIR", 5)) {
2155 adjust_prefix(bufp
, BONESPREFIX
);
2156 } else if (match_varname(buf
, "DATADIR", 4)) {
2157 adjust_prefix(bufp
, DATAPREFIX
);
2158 } else if (match_varname(buf
, "SCOREDIR", 4)) {
2159 adjust_prefix(bufp
, SCOREPREFIX
);
2160 } else if (match_varname(buf
, "LOCKDIR", 4)) {
2161 adjust_prefix(bufp
, LOCKPREFIX
);
2162 } else if (match_varname(buf
, "CONFIGDIR", 4)) {
2163 adjust_prefix(bufp
, CONFIGPREFIX
);
2164 } else if (match_varname(buf
, "TROUBLEDIR", 4)) {
2165 adjust_prefix(bufp
, TROUBLEPREFIX
);
2166 #else /*NOCWD_ASSUMPTIONS*/
2168 } else if (match_varname(buf
, "HACKDIR", 4)) {
2169 (void) strncpy(hackdir
, bufp
, PATHLEN
- 1);
2171 } else if (match_varname(buf
, "RAMDISK", 3)) {
2172 /* The following ifdef is NOT in the wrong
2173 * place. For now, we accept and silently
2176 if (strlen(bufp
) >= PATHLEN
)
2177 bufp
[PATHLEN
- 1] = '\0';
2178 Strcpy(levels
, bufp
);
2179 ramdisk
= (strcmp(permbones
, levels
) != 0);
2180 ramdisk_specified
= TRUE
;
2183 } else if (match_varname(buf
, "LEVELS", 4)) {
2184 if (strlen(bufp
) >= PATHLEN
)
2185 bufp
[PATHLEN
- 1] = '\0';
2186 Strcpy(permbones
, bufp
);
2187 if (!ramdisk_specified
|| !*levels
)
2188 Strcpy(levels
, bufp
);
2189 ramdisk
= (strcmp(permbones
, levels
) != 0);
2190 } else if (match_varname(buf
, "SAVE", 4)) {
2192 extern int saveprompt
;
2196 if ((ptr
= index(bufp
, ';')) != 0) {
2199 if (*(ptr
+ 1) == 'n' || *(ptr
+ 1) == 'N') {
2204 #if defined(SYSFLAGS) && defined(MFLOPPY)
2206 saveprompt
= sysflags
.asksavedisk
;
2209 (void) strncpy(SAVEP
, bufp
, SAVESIZE
- 1);
2210 append_slash(SAVEP
);
2212 #endif /*NOCWD_ASSUMPTIONS*/
2214 } else if (match_varname(buf
, "NAME", 4)) {
2215 (void) strncpy(plname
, bufp
, PL_NSIZ
- 1);
2216 } else if (match_varname(buf
, "ROLE", 4)
2217 || match_varname(buf
, "CHARACTER", 4)) {
2218 if ((len
= str2role(bufp
)) >= 0)
2219 flags
.initrole
= len
;
2220 } else if (match_varname(buf
, "DOGNAME", 3)) {
2221 (void) strncpy(dogname
, bufp
, PL_PSIZ
- 1);
2222 } else if (match_varname(buf
, "CATNAME", 3)) {
2223 (void) strncpy(catname
, bufp
, PL_PSIZ
- 1);
2226 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "WIZARDS", 7)) {
2228 free((genericptr_t
) sysopt
.wizards
);
2229 sysopt
.wizards
= dupstr(bufp
);
2230 if (strlen(sysopt
.wizards
) && strcmp(sysopt
.wizards
, "*")) {
2231 /* pre-format WIZARDS list now; it's displayed during a panic
2232 and since that panic might be due to running out of memory,
2233 we don't want to risk attempting to allocate any memory then */
2234 if (sysopt
.fmtd_wizard_list
)
2235 free((genericptr_t
) sysopt
.fmtd_wizard_list
);
2236 sysopt
.fmtd_wizard_list
= build_english_list(sysopt
.wizards
);
2238 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "SHELLERS", 8)) {
2239 if (sysopt
.shellers
)
2240 free((genericptr_t
) sysopt
.shellers
);
2241 sysopt
.shellers
= dupstr(bufp
);
2242 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "EXPLORERS", 7)) {
2243 if (sysopt
.explorers
)
2244 free((genericptr_t
) sysopt
.explorers
);
2245 sysopt
.explorers
= dupstr(bufp
);
2246 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "DEBUGFILES", 5)) {
2247 /* if showdebug() has already been called (perhaps we've added
2248 some debugpline() calls to option processing) and has found
2249 a value for getenv("DEBUGFILES"), don't override that */
2250 if (sysopt
.env_dbgfl
<= 0) {
2251 if (sysopt
.debugfiles
)
2252 free((genericptr_t
) sysopt
.debugfiles
);
2253 sysopt
.debugfiles
= dupstr(bufp
);
2255 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "SUPPORT", 7)) {
2257 free((genericptr_t
) sysopt
.support
);
2258 sysopt
.support
= dupstr(bufp
);
2259 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "RECOVER", 7)) {
2261 free((genericptr_t
) sysopt
.recover
);
2262 sysopt
.recover
= dupstr(bufp
);
2263 } else if (src
== SET_IN_SYS
2264 && match_varname(buf
, "CHECK_SAVE_UID", 14)) {
2266 sysopt
.check_save_uid
= n
;
2267 } else if (src
== SET_IN_SYS
2268 && match_varname(buf
, "CHECK_PLNAME", 12)) {
2270 sysopt
.check_plname
= n
;
2271 } else if (match_varname(buf
, "SEDUCE", 6)) {
2272 n
= !!atoi(bufp
); /* XXX this could be tighter */
2273 /* allow anyone to turn it off, but only sysconf to turn it on*/
2274 if (src
!= SET_IN_SYS
&& n
!= 0) {
2275 raw_printf("Illegal value in SEDUCE");
2279 sysopt_seduce_set(sysopt
.seduce
);
2280 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "MAXPLAYERS", 10)) {
2282 /* XXX to get more than 25, need to rewrite all lock code */
2283 if (n
< 0 || n
> 25) {
2284 raw_printf("Illegal value in MAXPLAYERS (maximum is 25).");
2287 sysopt
.maxplayers
= n
;
2288 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "PERSMAX", 7)) {
2291 raw_printf("Illegal value in PERSMAX (minimum is 1).");
2295 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "PERS_IS_UID", 11)) {
2297 if (n
!= 0 && n
!= 1) {
2298 raw_printf("Illegal value in PERS_IS_UID (must be 0 or 1).");
2301 sysopt
.pers_is_uid
= n
;
2302 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "ENTRYMAX", 8)) {
2305 raw_printf("Illegal value in ENTRYMAX (minimum is 10).");
2308 sysopt
.entrymax
= n
;
2309 } else if ((src
== SET_IN_SYS
) && match_varname(buf
, "POINTSMIN", 9)) {
2312 raw_printf("Illegal value in POINTSMIN (minimum is 1).");
2315 sysopt
.pointsmin
= n
;
2316 } else if (src
== SET_IN_SYS
2317 && match_varname(buf
, "MAX_STATUENAME_RANK", 10)) {
2321 "Illegal value in MAX_STATUENAME_RANK (minimum is 1).");
2324 sysopt
.tt_oname_maxrank
= n
;
2326 /* SYSCF PANICTRACE options */
2327 } else if (src
== SET_IN_SYS
2328 && match_varname(buf
, "PANICTRACE_LIBC", 15)) {
2330 #if defined(PANICTRACE) && defined(PANICTRACE_LIBC)
2331 if (n
< 0 || n
> 2) {
2332 raw_printf("Illegal value in PANICTRACE_LIBC (not 0,1,2).");
2336 sysopt
.panictrace_libc
= n
;
2337 } else if (src
== SET_IN_SYS
2338 && match_varname(buf
, "PANICTRACE_GDB", 14)) {
2340 #if defined(PANICTRACE)
2341 if (n
< 0 || n
> 2) {
2342 raw_printf("Illegal value in PANICTRACE_GDB (not 0,1,2).");
2346 sysopt
.panictrace_gdb
= n
;
2347 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "GDBPATH", 7)) {
2348 #if defined(PANICTRACE) && !defined(VMS)
2349 if (!file_exists(bufp
)) {
2350 raw_printf("File specified in GDBPATH does not exist.");
2355 free((genericptr_t
) sysopt
.gdbpath
);
2356 sysopt
.gdbpath
= dupstr(bufp
);
2357 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "GREPPATH", 7)) {
2358 #if defined(PANICTRACE) && !defined(VMS)
2359 if (!file_exists(bufp
)) {
2360 raw_printf("File specified in GREPPATH does not exist.");
2364 if (sysopt
.greppath
)
2365 free((genericptr_t
) sysopt
.greppath
);
2366 sysopt
.greppath
= dupstr(bufp
);
2369 } else if (match_varname(buf
, "BOULDER", 3)) {
2370 (void) get_uchars(fp
, buf
, bufp
, &iflags
.bouldersym
, TRUE
, 1,
2372 } else if (match_varname(buf
, "MENUCOLOR", 9)) {
2373 (void) add_menu_coloring(bufp
);
2374 } else if (match_varname(buf
, "WARNINGS", 5)) {
2375 (void) get_uchars(fp
, buf
, bufp
, translate
, FALSE
, WARNCOUNT
,
2377 assign_warnings(translate
);
2378 } else if (match_varname(buf
, "SYMBOLS", 4)) {
2379 char *op
, symbuf
[BUFSZ
];
2383 /* check for line continuation (trailing '\') */
2385 morelines
= (--op
>= bufp
&& *op
== '\\');
2388 /* strip trailing space now that '\' is gone */
2389 if (--op
>= bufp
&& *op
== ' ')
2393 if (!parsesymbols(bufp
)) {
2394 raw_printf("Error in SYMBOLS definition '%s'.\n", bufp
);
2400 if (!fgets(symbuf
, BUFSZ
, fp
)) {
2406 } while (*bufp
== '#');
2408 } while (morelines
);
2409 switch_symbols(TRUE
);
2410 } else if (match_varname(buf
, "WIZKIT", 6)) {
2411 (void) strncpy(wizkit
, bufp
, WIZKIT_MAX
- 1);
2413 } else if (match_varname(buf
, "FONT", 4)) {
2416 if (t
= strchr(buf
+ 5, ':')) {
2418 amii_set_text_font(buf
+ 5, atoi(t
+ 1));
2421 } else if (match_varname(buf
, "PATH", 4)) {
2422 (void) strncpy(PATH
, bufp
, PATHLEN
- 1);
2423 } else if (match_varname(buf
, "DEPTH", 5)) {
2424 extern int amii_numcolors
;
2425 int val
= atoi(bufp
);
2427 amii_numcolors
= 1L << min(DEPTH
, val
);
2429 } else if (match_varname(buf
, "DRIPENS", 7)) {
2433 for (i
= 0, t
= strtok(bufp
, ",/"); t
!= (char *) 0;
2434 i
< 20 && (t
= strtok((char *) 0, ",/")), ++i
) {
2435 sscanf(t
, "%d", &val
);
2436 sysflags
.amii_dripens
[i
] = val
;
2439 } else if (match_varname(buf
, "SCREENMODE", 10)) {
2440 extern long amii_scrnmode
;
2442 if (!stricmp(bufp
, "req"))
2443 amii_scrnmode
= 0xffffffff; /* Requester */
2444 else if (sscanf(bufp
, "%x", &amii_scrnmode
) != 1)
2446 } else if (match_varname(buf
, "MSGPENS", 7)) {
2447 extern int amii_msgAPen
, amii_msgBPen
;
2448 char *t
= strtok(bufp
, ",/");
2451 sscanf(t
, "%d", &amii_msgAPen
);
2452 if (t
= strtok((char *) 0, ",/"))
2453 sscanf(t
, "%d", &amii_msgBPen
);
2455 } else if (match_varname(buf
, "TEXTPENS", 8)) {
2456 extern int amii_textAPen
, amii_textBPen
;
2457 char *t
= strtok(bufp
, ",/");
2460 sscanf(t
, "%d", &amii_textAPen
);
2461 if (t
= strtok((char *) 0, ",/"))
2462 sscanf(t
, "%d", &amii_textBPen
);
2464 } else if (match_varname(buf
, "MENUPENS", 8)) {
2465 extern int amii_menuAPen
, amii_menuBPen
;
2466 char *t
= strtok(bufp
, ",/");
2469 sscanf(t
, "%d", &amii_menuAPen
);
2470 if (t
= strtok((char *) 0, ",/"))
2471 sscanf(t
, "%d", &amii_menuBPen
);
2473 } else if (match_varname(buf
, "STATUSPENS", 10)) {
2474 extern int amii_statAPen
, amii_statBPen
;
2475 char *t
= strtok(bufp
, ",/");
2478 sscanf(t
, "%d", &amii_statAPen
);
2479 if (t
= strtok((char *) 0, ",/"))
2480 sscanf(t
, "%d", &amii_statBPen
);
2482 } else if (match_varname(buf
, "OTHERPENS", 9)) {
2483 extern int amii_otherAPen
, amii_otherBPen
;
2484 char *t
= strtok(bufp
, ",/");
2487 sscanf(t
, "%d", &amii_otherAPen
);
2488 if (t
= strtok((char *) 0, ",/"))
2489 sscanf(t
, "%d", &amii_otherBPen
);
2491 } else if (match_varname(buf
, "PENS", 4)) {
2492 extern unsigned short amii_init_map
[AMII_MAXCOLORS
];
2496 for (i
= 0, t
= strtok(bufp
, ",/");
2497 i
< AMII_MAXCOLORS
&& t
!= (char *) 0;
2498 t
= strtok((char *) 0, ",/"), ++i
) {
2499 sscanf(t
, "%hx", &amii_init_map
[i
]);
2501 amii_setpens(amii_numcolors
= i
);
2502 } else if (match_varname(buf
, "FGPENS", 6)) {
2503 extern int foreg
[AMII_MAXCOLORS
];
2507 for (i
= 0, t
= strtok(bufp
, ",/");
2508 i
< AMII_MAXCOLORS
&& t
!= (char *) 0;
2509 t
= strtok((char *) 0, ",/"), ++i
) {
2510 sscanf(t
, "%d", &foreg
[i
]);
2512 } else if (match_varname(buf
, "BGPENS", 6)) {
2513 extern int backg
[AMII_MAXCOLORS
];
2517 for (i
= 0, t
= strtok(bufp
, ",/");
2518 i
< AMII_MAXCOLORS
&& t
!= (char *) 0;
2519 t
= strtok((char *) 0, ",/"), ++i
) {
2520 sscanf(t
, "%d", &backg
[i
]);
2524 } else if (match_varname(buf
, "SOUNDDIR", 8)) {
2525 sounddir
= dupstr(bufp
);
2526 } else if (match_varname(buf
, "SOUND", 5)) {
2527 add_sound_mapping(bufp
);
2530 /* These should move to wc_ options */
2531 } else if (match_varname(buf
, "QT_TILEWIDTH", 12)) {
2532 extern char *qt_tilewidth
;
2534 if (qt_tilewidth
== NULL
)
2535 qt_tilewidth
= dupstr(bufp
);
2536 } else if (match_varname(buf
, "QT_TILEHEIGHT", 13)) {
2537 extern char *qt_tileheight
;
2539 if (qt_tileheight
== NULL
)
2540 qt_tileheight
= dupstr(bufp
);
2541 } else if (match_varname(buf
, "QT_FONTSIZE", 11)) {
2542 extern char *qt_fontsize
;
2544 if (qt_fontsize
== NULL
)
2545 qt_fontsize
= dupstr(bufp
);
2546 } else if (match_varname(buf
, "QT_COMPACT", 10)) {
2547 extern int qt_compact_mode
;
2549 qt_compact_mode
= atoi(bufp
);
2558 can_read_file(filename
)
2559 const char *filename
;
2561 return (boolean
) (access(filename
, 4) == 0);
2563 #endif /* USER_SOUNDS */
2566 read_config_file(filename
, src
)
2567 const char *filename
;
2570 char buf
[4 * BUFSZ
];
2572 boolean rv
= TRUE
; /* assume successful parse */
2574 if (!(fp
= fopen_config_file(filename
, src
)))
2577 /* begin detection of duplicate configfile options */
2578 set_duplicate_opt_detection(1);
2580 while (fgets(buf
, sizeof buf
, fp
)) {
2583 XXX Don't call read() in parse_config_line, read as callback or reassemble
2585 OR: Forbid multiline stuff for alternate config sources.
2588 if (!parse_config_line(fp
, strip_newline(buf
), src
)) {
2589 static const char badoptionline
[] = "Bad option line: \"%s\"";
2591 /* truncate buffer if it's long; this is actually conservative */
2592 if (strlen(buf
) > BUFSZ
- sizeof badoptionline
)
2593 buf
[BUFSZ
- sizeof badoptionline
] = '\0';
2595 raw_printf(badoptionline
, buf
);
2602 /* turn off detection of duplicate configfile options */
2603 set_duplicate_opt_detection(0);
2611 #if defined(VMS) || defined(UNIX)
2612 char tmp_wizkit
[BUFSZ
];
2616 envp
= nh_getenv("WIZKIT");
2618 (void) strncpy(wizkit
, envp
, WIZKIT_MAX
- 1);
2623 if (access(wizkit
, 4) == -1) {
2624 /* 4 is R_OK on newer systems */
2625 /* nasty sneaky attempt to read file through
2626 * NetHack's setuid permissions -- this is a
2627 * place a file name may be wholly under the player's
2630 raw_printf("Access to %s denied (%d).", wizkit
, errno
);
2632 /* fall through to standard names */
2635 if ((fp
= fopenp(wizkit
, "r")) != (FILE *) 0) {
2637 #if defined(UNIX) || defined(VMS)
2639 /* access() above probably caught most problems for UNIX */
2640 raw_printf("Couldn't open requested config file %s (%d).", wizkit
,
2646 #if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32)
2647 if ((fp
= fopenp(fqname(wizkit
, CONFIGPREFIX
, 0), "r")) != (FILE *) 0)
2651 envp
= nh_getenv("HOME");
2653 Sprintf(tmp_wizkit
, "%s%s", envp
, wizkit
);
2655 Sprintf(tmp_wizkit
, "%s%s", "sys$login:", wizkit
);
2656 if ((fp
= fopenp(tmp_wizkit
, "r")) != (FILE *) 0)
2658 #else /* should be only UNIX left */
2659 envp
= nh_getenv("HOME");
2661 Sprintf(tmp_wizkit
, "%s/%s", envp
, wizkit
);
2663 Strcpy(tmp_wizkit
, wizkit
);
2664 if ((fp
= fopenp(tmp_wizkit
, "r")) != (FILE *) 0)
2666 else if (errno
!= ENOENT
) {
2667 /* e.g., problems when setuid NetHack can't search home
2668 * directory restricted to user */
2669 raw_printf("Couldn't open default wizkit file %s (%d).", tmp_wizkit
,
2678 /* add to hero's inventory if there's room, otherwise put item on floor */
2683 if (!obj
|| obj
== &zeroobj
)
2686 /* subset of starting inventory pre-ID */
2688 if (Role_if(PM_PRIEST
))
2690 /* same criteria as lift_object()'s check for available inventory slot */
2691 if (obj
->oclass
!= COIN_CLASS
&& inv_cnt(FALSE
) >= 52
2692 && !merge_choice(invent
, obj
)) {
2693 /* inventory overflow; can't just place & stack object since
2694 hero isn't in position yet, so schedule for arrival later */
2695 add_to_migration(obj
);
2696 obj
->ox
= 0; /* index of main dungeon */
2697 obj
->oy
= 1; /* starting level number */
2699 (long) (MIGR_WITH_HERO
| MIGR_NOBREAK
| MIGR_NOSCATTER
);
2709 char *ep
, buf
[BUFSZ
];
2711 boolean bad_items
= FALSE
, skip
= FALSE
;
2713 if (!wizard
|| !(fp
= fopen_wizkit_file()))
2716 program_state
.wizkit_wishing
= 1;
2717 while (fgets(buf
, (int) (sizeof buf
), fp
)) {
2718 ep
= index(buf
, '\n');
2719 if (skip
) { /* in case previous line was too long */
2721 skip
= FALSE
; /* found newline; next line is normal */
2724 skip
= TRUE
; /* newline missing; discard next fgets */
2726 *ep
= '\0'; /* remove newline */
2729 otmp
= readobjnam(buf
, (struct obj
*) 0);
2731 if (otmp
!= &zeroobj
)
2732 wizkit_addinv(otmp
);
2734 /* .60 limits output line width to 79 chars */
2735 raw_printf("Bad wizkit item: \"%.60s\"", buf
);
2741 program_state
.wizkit_wishing
= 0;
2748 extern struct symsetentry
*symset_list
; /* options.c */
2749 extern struct symparse loadsyms
[]; /* drawing.c */
2750 extern const char *known_handling
[]; /* drawing.c */
2751 extern const char *known_restrictions
[]; /* drawing.c */
2752 static int symset_count
= 0; /* for pick-list building only */
2753 static boolean chosen_symset_start
= FALSE
, chosen_symset_end
= FALSE
;
2761 fp
= fopen_datafile(SYMBOLS
, "r", HACKPREFIX
);
2766 * Returns 1 if the chose symset was found and loaded.
2767 * 0 if it wasn't found in the sym file or other problem.
2770 read_sym_file(which_set
)
2773 char buf
[4 * BUFSZ
];
2776 if (!(fp
= fopen_sym_file()))
2780 chosen_symset_start
= chosen_symset_end
= FALSE
;
2781 while (fgets(buf
, 4 * BUFSZ
, fp
)) {
2782 if (!parse_sym_line(buf
, which_set
)) {
2783 raw_printf("Bad symbol line: \"%.50s\"", buf
);
2788 if (!chosen_symset_start
&& !chosen_symset_end
) {
2789 /* name caller put in symset[which_set].name was not found;
2790 if it looks like "Default symbols", null it out and return
2791 success to use the default; otherwise, return failure */
2792 if (symset
[which_set
].name
2793 && (fuzzymatch(symset
[which_set
].name
, "Default symbols",
2795 || !strcmpi(symset
[which_set
].name
, "default")))
2796 clear_symsetentry(which_set
, TRUE
);
2797 return (symset
[which_set
].name
== 0) ? 1 : 0;
2799 if (!chosen_symset_end
) {
2800 raw_printf("Missing finish for symset \"%s\"",
2801 symset
[which_set
].name
? symset
[which_set
].name
2808 /* returns 0 on error */
2810 parse_sym_line(buf
, which_set
)
2815 struct symparse
*symp
= (struct symparse
*) 0;
2816 char *bufp
, *commentp
, *altp
;
2818 /* convert each instance of whitespace (tabs, consecutive spaces)
2819 into a single space; leading and trailing spaces are stripped */
2821 if (!*buf
|| *buf
== '#' || !strcmp(buf
, " "))
2823 /* remove trailing comment, if any */
2824 if ((commentp
= rindex(buf
, '#')) != 0) {
2826 /* remove space preceding the stripped comment, if any;
2827 we know 'commentp > buf' because *buf=='#' was caught above */
2828 if (commentp
[-1] == ' ')
2832 /* find the '=' or ':' */
2833 bufp
= index(buf
, '=');
2834 altp
= index(buf
, ':');
2835 if (!bufp
|| (altp
&& altp
< bufp
))
2838 if (strncmpi(buf
, "finish", 6) == 0) {
2839 /* end current graphics set */
2840 if (chosen_symset_start
)
2841 chosen_symset_end
= TRUE
;
2842 chosen_symset_start
= FALSE
;
2847 /* skip '=' and space which follows, if any */
2852 symp
= match_sym(buf
);
2856 if (!symset
[which_set
].name
) {
2857 /* A null symset name indicates that we're just
2858 building a pick-list of possible symset
2859 values from the file, so only do that */
2860 if (symp
->range
== SYM_CONTROL
) {
2861 struct symsetentry
*tmpsp
;
2863 switch (symp
->idx
) {
2866 (struct symsetentry
*) alloc(sizeof (struct symsetentry
));
2867 tmpsp
->next
= (struct symsetentry
*) 0;
2869 symset_list
= tmpsp
;
2873 tmpsp
->next
= symset_list
;
2874 symset_list
= tmpsp
;
2876 tmpsp
->idx
= symset_count
;
2877 tmpsp
->name
= dupstr(bufp
);
2878 tmpsp
->desc
= (char *) 0;
2880 /* initialize restriction bits */
2885 /* handler type identified */
2886 tmpsp
= symset_list
; /* most recent symset */
2887 tmpsp
->handling
= H_UNK
;
2889 while (known_handling
[i
]) {
2890 if (!strcmpi(known_handling
[i
], bufp
)) {
2891 tmpsp
->handling
= i
;
2892 break; /* while loop */
2897 case 3: /* description:something */
2898 tmpsp
= symset_list
; /* most recent symset */
2899 if (tmpsp
&& !tmpsp
->desc
)
2900 tmpsp
->desc
= dupstr(bufp
);
2903 /* restrictions: xxxx*/
2904 tmpsp
= symset_list
; /* most recent symset */
2905 for (i
= 0; known_restrictions
[i
]; ++i
) {
2906 if (!strcmpi(known_restrictions
[i
], bufp
)) {
2915 break; /* while loop */
2924 if (symp
->range
== SYM_CONTROL
) {
2925 switch (symp
->idx
) {
2927 /* start of symset */
2928 if (!strcmpi(bufp
, symset
[which_set
].name
)) {
2929 /* matches desired one */
2930 chosen_symset_start
= TRUE
;
2931 /* these init_*() functions clear symset fields too */
2932 if (which_set
== ROGUESET
)
2934 else if (which_set
== PRIMARY
)
2940 if (chosen_symset_start
)
2941 chosen_symset_end
= TRUE
;
2942 chosen_symset_start
= FALSE
;
2945 /* handler type identified */
2946 if (chosen_symset_start
)
2947 set_symhandling(bufp
, which_set
);
2949 /* case 3: (description) is ignored here */
2950 case 4: /* color:off */
2951 if (chosen_symset_start
) {
2953 if (!strcmpi(bufp
, "true") || !strcmpi(bufp
, "yes")
2954 || !strcmpi(bufp
, "on"))
2955 symset
[which_set
].nocolor
= 0;
2956 else if (!strcmpi(bufp
, "false")
2957 || !strcmpi(bufp
, "no")
2958 || !strcmpi(bufp
, "off"))
2959 symset
[which_set
].nocolor
= 1;
2963 case 5: /* restrictions: xxxx*/
2964 if (chosen_symset_start
) {
2967 while (known_restrictions
[n
]) {
2968 if (!strcmpi(known_restrictions
[n
], bufp
)) {
2971 symset
[which_set
].primary
= 1;
2974 symset
[which_set
].rogue
= 1;
2977 break; /* while loop */
2984 } else { /* !SYM_CONTROL */
2985 val
= sym_val(bufp
);
2986 if (chosen_symset_start
) {
2987 if (which_set
== PRIMARY
) {
2988 update_l_symset(symp
, val
);
2989 } else if (which_set
== ROGUESET
) {
2990 update_r_symset(symp
, val
);
2999 set_symhandling(handling
, which_set
)
3005 symset
[which_set
].handling
= H_UNK
;
3006 while (known_handling
[i
]) {
3007 if (!strcmpi(known_handling
[i
], handling
)) {
3008 symset
[which_set
].handling
= i
;
3015 /* ---------- END CONFIG FILE HANDLING ----------- */
3017 /* ---------- BEGIN SCOREBOARD CREATION ----------- */
3020 #define UNUSED_if_not_OS2_CODEVIEW /*empty*/
3022 #define UNUSED_if_not_OS2_CODEVIEW UNUSED
3025 /* verify that we can write to scoreboard file; if not, try to create one */
3028 check_recordfile(dir
)
3029 const char *dir UNUSED_if_not_OS2_CODEVIEW
;
3031 #if defined(PRAGMA_UNUSED) && !defined(OS2_CODEVIEW)
3034 const char *fq_record
;
3037 #if defined(UNIX) || defined(VMS)
3038 fq_record
= fqname(RECORD
, SCOREPREFIX
, 0);
3039 fd
= open(fq_record
, O_RDWR
, 0);
3041 #ifdef VMS /* must be stream-lf to use UPDATE_RECORD_IN_PLACE */
3042 if (!file_is_stmlf(fd
)) {
3044 "Warning: scoreboard file %s is not in stream_lf format",
3049 (void) nhclose(fd
); /* RECORD is accessible */
3050 } else if ((fd
= open(fq_record
, O_CREAT
| O_RDWR
, FCMASK
)) >= 0) {
3051 (void) nhclose(fd
); /* RECORD newly created */
3052 #if defined(VMS) && !defined(SECURE)
3053 /* Re-protect RECORD with world:read+write+execute+delete access. */
3054 (void) chmod(fq_record
, FCMASK
| 007);
3055 #endif /* VMS && !SECURE */
3057 raw_printf("Warning: cannot write scoreboard file %s", fq_record
);
3060 #endif /* !UNIX && !VMS */
3061 #if defined(MICRO) || defined(WIN32)
3064 #ifdef OS2_CODEVIEW /* explicit path on opening for OS/2 */
3065 /* how does this work when there isn't an explicit path or fopenp
3066 * for later access to the file via fopen_datafile? ? */
3067 (void) strncpy(tmp
, dir
, PATHLEN
- 1);
3068 tmp
[PATHLEN
- 1] = '\0';
3069 if ((strlen(tmp
) + 1 + strlen(RECORD
)) < (PATHLEN
- 1)) {
3071 Strcat(tmp
, RECORD
);
3075 Strcpy(tmp
, RECORD
);
3076 fq_record
= fqname(RECORD
, SCOREPREFIX
, 0);
3079 if ((fd
= open(fq_record
, O_RDWR
)) < 0) {
3080 /* try to create empty record */
3081 #if defined(AZTEC_C) || defined(_DCC) \
3082 || (defined(__GNUC__) && defined(__AMIGA__))
3083 /* Aztec doesn't use the third argument */
3084 /* DICE doesn't like it */
3085 if ((fd
= open(fq_record
, O_CREAT
| O_RDWR
)) < 0) {
3087 if ((fd
= open(fq_record
, O_CREAT
| O_RDWR
, S_IREAD
| S_IWRITE
))
3090 raw_printf("Warning: cannot write record %s", tmp
);
3094 } else /* open succeeded */
3096 #else /* MICRO || WIN32*/
3099 /* Create the "record" file, if necessary */
3100 fq_record
= fqname(RECORD
, SCOREPREFIX
, 0);
3101 fd
= macopen(fq_record
, O_RDWR
| O_CREAT
, TEXT_TYPE
);
3106 #endif /* MICRO || WIN32*/
3109 /* ---------- END SCOREBOARD CREATION ----------- */
3111 /* ---------- BEGIN PANIC/IMPOSSIBLE LOG ----------- */
3115 paniclog(type
, reason
)
3116 const char *type
; /* panic, impossible, trickery */
3117 const char *reason
; /* explanation */
3123 if (!program_state
.in_paniclog
) {
3124 program_state
.in_paniclog
= 1;
3125 lfile
= fopen_datafile(PANICLOG
, "a", TROUBLEPREFIX
);
3127 #ifdef PANICLOG_FMT2
3128 (void) fprintf(lfile
, "%ld %s: %s %s\n",
3129 ubirthday
, (plname
? plname
: "(none)"),
3132 time_t now
= getnow();
3134 char playmode
= wizard
? 'D' : discover
? 'X' : '-';
3136 (void) fprintf(lfile
, "%s %08ld %06ld %d %c: %s %s\n",
3137 version_string(buf
), yyyymmdd(now
), hhmmss(now
),
3138 uid
, playmode
, type
, reason
);
3139 #endif /* !PANICLOG_FMT2 */
3140 (void) fclose(lfile
);
3142 program_state
.in_paniclog
= 0;
3144 #endif /* PANICLOG */
3148 /* ---------- END PANIC/IMPOSSIBLE LOG ----------- */
3152 /* ---------- BEGIN INTERNAL RECOVER ----------- */
3157 int lev
, savelev
, hpid
, pltmpsiz
;
3159 struct version_info version_data
;
3161 char savename
[SAVESIZE
], errbuf
[BUFSZ
];
3162 struct savefile_info sfi
;
3163 char tmpplbuf
[PL_NSIZ
];
3165 for (lev
= 0; lev
< 256; lev
++)
3168 /* level 0 file contains:
3169 * pid of creating process (ignored here)
3170 * level number for current level of save file
3171 * name of save file nethack would have created
3176 gfd
= open_levelfile(0, errbuf
);
3178 raw_printf("%s\n", errbuf
);
3181 if (read(gfd
, (genericptr_t
) &hpid
, sizeof hpid
) != sizeof hpid
) {
3182 raw_printf("\n%s\n%s\n",
3183 "Checkpoint data incompletely written or subsequently clobbered.",
3184 "Recovery impossible.");
3185 (void) nhclose(gfd
);
3188 if (read(gfd
, (genericptr_t
) &savelev
, sizeof(savelev
))
3189 != sizeof(savelev
)) {
3191 "\nCheckpointing was not in effect for %s -- recovery impossible.\n",
3193 (void) nhclose(gfd
);
3196 if ((read(gfd
, (genericptr_t
) savename
, sizeof savename
)
3198 || (read(gfd
, (genericptr_t
) &version_data
, sizeof version_data
)
3199 != sizeof version_data
)
3200 || (read(gfd
, (genericptr_t
) &sfi
, sizeof sfi
) != sizeof sfi
)
3201 || (read(gfd
, (genericptr_t
) &pltmpsiz
, sizeof pltmpsiz
)
3202 != sizeof pltmpsiz
) || (pltmpsiz
> PL_NSIZ
)
3203 || (read(gfd
, (genericptr_t
) &tmpplbuf
, pltmpsiz
) != pltmpsiz
)) {
3204 raw_printf("\nError reading %s -- can't recover.\n", lock
);
3205 (void) nhclose(gfd
);
3209 /* save file should contain:
3213 * current level (including pets)
3214 * (non-level-based) game state
3217 set_savefile_name(TRUE
);
3218 sfd
= create_savefile();
3220 raw_printf("\nCannot recover savefile %s.\n", SAVEF
);
3221 (void) nhclose(gfd
);
3225 lfd
= open_levelfile(savelev
, errbuf
);
3227 raw_printf("\n%s\n", errbuf
);
3228 (void) nhclose(gfd
);
3229 (void) nhclose(sfd
);
3234 if (write(sfd
, (genericptr_t
) &version_data
, sizeof version_data
)
3235 != sizeof version_data
) {
3236 raw_printf("\nError writing %s; recovery failed.", SAVEF
);
3237 (void) nhclose(gfd
);
3238 (void) nhclose(sfd
);
3243 if (write(sfd
, (genericptr_t
) &sfi
, sizeof sfi
) != sizeof sfi
) {
3244 raw_printf("\nError writing %s; recovery failed (savefile_info).\n",
3246 (void) nhclose(gfd
);
3247 (void) nhclose(sfd
);
3252 if (write(sfd
, (genericptr_t
) &pltmpsiz
, sizeof pltmpsiz
)
3253 != sizeof pltmpsiz
) {
3254 raw_printf("Error writing %s; recovery failed (player name size).\n",
3256 (void) nhclose(gfd
);
3257 (void) nhclose(sfd
);
3262 if (write(sfd
, (genericptr_t
) &tmpplbuf
, pltmpsiz
) != pltmpsiz
) {
3263 raw_printf("Error writing %s; recovery failed (player name).\n",
3265 (void) nhclose(gfd
);
3266 (void) nhclose(sfd
);
3271 if (!copy_bytes(lfd
, sfd
)) {
3272 (void) nhclose(lfd
);
3273 (void) nhclose(sfd
);
3277 (void) nhclose(lfd
);
3278 processed
[savelev
] = 1;
3280 if (!copy_bytes(gfd
, sfd
)) {
3281 (void) nhclose(lfd
);
3282 (void) nhclose(sfd
);
3286 (void) nhclose(gfd
);
3289 for (lev
= 1; lev
< 256; lev
++) {
3290 /* level numbers are kept in xchars in save.c, so the
3291 * maximum level number (for the endlevel) must be < 256
3293 if (lev
!= savelev
) {
3294 lfd
= open_levelfile(lev
, (char *) 0);
3296 /* any or all of these may not exist */
3298 write(sfd
, (genericptr_t
) &levc
, sizeof(levc
));
3299 if (!copy_bytes(lfd
, sfd
)) {
3300 (void) nhclose(lfd
);
3301 (void) nhclose(sfd
);
3305 (void) nhclose(lfd
);
3310 (void) nhclose(sfd
);
3312 #ifdef HOLD_LOCKFILE_OPEN
3316 * We have a successful savefile!
3317 * Only now do we erase the level files.
3319 for (lev
= 0; lev
< 256; lev
++) {
3320 if (processed
[lev
]) {
3321 const char *fq_lock
;
3322 set_levelfile_name(lock
, lev
);
3323 fq_lock
= fqname(lock
, LEVELPREFIX
, 3);
3324 (void) unlink(fq_lock
);
3331 copy_bytes(ifd
, ofd
)
3338 nfrom
= read(ifd
, buf
, BUFSIZ
);
3339 nto
= write(ofd
, buf
, nfrom
);
3342 } while (nfrom
== BUFSIZ
);
3346 /* ---------- END INTERNAL RECOVER ----------- */
3347 #endif /*SELF_RECOVER*/
3349 /* ---------- OTHER ----------- */
3359 * All we really care about is the end result - can we read the file?
3360 * So just check that directly.
3362 * Not tested on most of the old platforms (which don't attempt
3363 * to implement SYSCF).
3364 * Some ports don't like open()'s optional third argument;
3365 * VMS overrides open() usage with a macro which requires it.
3368 # if defined(NOCWD_ASSUMPTIONS) && defined(WIN32)
3369 fd
= open(fqname(SYSCF_FILE
, SYSCONFPREFIX
, 0), O_RDONLY
);
3371 fd
= open(SYSCF_FILE
, O_RDONLY
);
3374 fd
= open(SYSCF_FILE
, O_RDONLY
, 0);
3381 raw_printf("Unable to open SYSCF_FILE.\n");
3385 #endif /* SYSCF_FILE */
3389 /* used by debugpline() to decide whether to issue a message
3390 * from a particular source file; caller passes __FILE__ and we check
3391 * whether it is in the source file list supplied by SYSCF's DEBUGFILES
3393 * pass FALSE to override wildcard matching; useful for files
3394 * like dungeon.c and questpgr.c, which generate a ridiculous amount of
3395 * output if DEBUG is defined and effectively block the use of a wildcard */
3397 debugcore(filename
, wildcards
)
3398 const char *filename
;
3401 const char *debugfiles
, *p
;
3403 if (!filename
|| !*filename
)
3404 return FALSE
; /* sanity precaution */
3406 if (sysopt
.env_dbgfl
== 0) {
3407 /* check once for DEBUGFILES in the environment;
3408 if found, it supersedes the sysconf value
3409 [note: getenv() rather than nh_getenv() since a long value
3410 is valid and doesn't pose any sort of overflow risk here] */
3411 if ((p
= getenv("DEBUGFILES")) != 0) {
3412 if (sysopt
.debugfiles
)
3413 free((genericptr_t
) sysopt
.debugfiles
);
3414 sysopt
.debugfiles
= dupstr(p
);
3415 sysopt
.env_dbgfl
= 1;
3417 sysopt
.env_dbgfl
= -1;
3420 debugfiles
= sysopt
.debugfiles
;
3421 /* usual case: sysopt.debugfiles will be empty */
3422 if (!debugfiles
|| !*debugfiles
)
3425 /* strip filename's path if present */
3427 if ((p
= rindex(filename
, '/')) != 0)
3431 filename
= vms_basename(filename
);
3432 /* vms_basename strips off 'type' suffix as well as path and version;
3433 we want to put suffix back (".c" assumed); since it always returns
3434 a pointer to a static buffer, we can safely modify its result */
3435 Strcat((char *) filename
, ".c");
3439 * Wildcard match will only work if there's a single pattern (which
3440 * might be a single file name without any wildcarding) rather than
3441 * a space-separated list.
3442 * [to NOT do: We could step through the space-separated list and
3443 * attempt a wildcard match against each element, but that would be
3444 * overkill for the intended usage.]
3446 if (wildcards
&& pmatch(debugfiles
, filename
))
3449 /* check whether filename is an element of the list */
3450 if ((p
= strstr(debugfiles
, filename
)) != 0) {
3451 int l
= (int) strlen(filename
);
3453 if ((p
== debugfiles
|| p
[-1] == ' ' || p
[-1] == '/')
3454 && (p
[l
] == ' ' || p
[l
] == '\0'))
3462 /* ---------- BEGIN TRIBUTE ----------- */
3467 #define SECTIONSCOPE 1
3468 #define TITLESCOPE 2
3469 #define PASSAGESCOPE 3
3471 #define MAXPASSAGES SIZE(context.novel.pasg) /* 20 */
3473 static int FDECL(choose_passage
, (int, unsigned));
3475 /* choose a random passage that hasn't been chosen yet; once all have
3476 been chosen, reset the tracking to make all passages available again */
3478 choose_passage(passagecnt
, oid
)
3479 int passagecnt
; /* total of available passages */
3480 unsigned oid
; /* book.o_id, used to determine whether re-reading same book */
3487 /* if a different book or we've used up all the passages already,
3488 reset in order to have all 'passagecnt' passages available */
3489 if (oid
!= context
.novel
.id
|| context
.novel
.count
== 0) {
3490 int i
, range
= passagecnt
, limit
= MAXPASSAGES
;
3492 context
.novel
.id
= oid
;
3493 if (range
<= limit
) {
3494 /* collect all of the N indices */
3495 context
.novel
.count
= passagecnt
;
3496 for (idx
= 0; idx
< MAXPASSAGES
; idx
++)
3497 context
.novel
.pasg
[idx
] = (xchar
) ((idx
< passagecnt
)
3500 /* collect MAXPASSAGES of the N indices */
3501 context
.novel
.count
= MAXPASSAGES
;
3502 for (idx
= i
= 0; i
< passagecnt
; ++i
, --range
)
3503 if (range
> 0 && rn2(range
) < limit
) {
3504 context
.novel
.pasg
[idx
++] = (xchar
) (i
+ 1);
3510 idx
= rn2(context
.novel
.count
);
3511 res
= (int) context
.novel
.pasg
[idx
];
3512 /* move the last slot's passage index into the slot just used
3513 and reduce the number of passages available */
3514 context
.novel
.pasg
[idx
] = context
.novel
.pasg
[--context
.novel
.count
];
3518 /* Returns True if you were able to read something. */
3520 read_tribute(tribsection
, tribtitle
, tribpassage
, nowin_buf
, bufsz
, oid
)
3521 const char *tribsection
, *tribtitle
;
3522 int tribpassage
, bufsz
;
3524 unsigned oid
; /* book identifier */
3527 char line
[BUFSZ
], lastline
[BUFSZ
];
3530 int linect
= 0, passagecnt
= 0, targetpassage
= 0;
3531 const char *badtranslation
= "an incomprehensible foreign translation";
3532 boolean matchedsection
= FALSE
, matchedtitle
= FALSE
;
3533 winid tribwin
= WIN_ERR
;
3534 boolean grasped
= FALSE
;
3535 boolean foundpassage
= FALSE
;
3540 /* check for mandatories */
3541 if (!tribsection
|| !tribtitle
) {
3543 pline("It's %s of \"%s\"!", badtranslation
, tribtitle
);
3547 debugpline3("read_tribute %s, %s, %d.", tribsection
, tribtitle
,
3550 fp
= dlb_fopen(TRIBUTEFILE
, "r");
3552 /* this is actually an error - cannot open tribute file! */
3554 pline("You feel too overwhelmed to continue!");
3559 * Syntax (not case-sensitive):
3562 * In the books section:
3563 * %title booktitle (n)
3564 * where booktitle=book title without quotes
3565 * (n)= total number of passages present for this title
3567 * where k=sequential passage number
3569 * %e ends the passage/book/section
3570 * If in a passage, it marks the end of that passage.
3571 * If in a book, it marks the end of that book.
3572 * If in a section, it marks the end of that section.
3577 *line
= *lastline
= '\0';
3578 while (dlb_fgets(line
, sizeof line
, fp
) != 0) {
3580 (void) strip_newline(line
);
3583 if (!strncmpi(&line
[1], "section ", sizeof "section " - 1)) {
3584 char *st
= &line
[9]; /* 9 from "%section " */
3586 scope
= SECTIONSCOPE
;
3587 matchedsection
= !strcmpi(st
, tribsection
) ? TRUE
: FALSE
;
3588 } else if (!strncmpi(&line
[1], "title ", sizeof "title " - 1)) {
3589 char *st
= &line
[7]; /* 7 from "%title " */
3592 if ((p1
= index(st
, '(')) != 0) {
3594 (void) mungspaces(st
);
3595 if ((p2
= index(p1
, ')')) != 0) {
3597 passagecnt
= atoi(p1
);
3599 if (matchedsection
&& !strcmpi(st
, tribtitle
)) {
3600 matchedtitle
= TRUE
;
3601 targetpassage
= !tribpassage
3602 ? choose_passage(passagecnt
, oid
)
3603 : (tribpassage
<= passagecnt
)
3606 matchedtitle
= FALSE
;
3610 } else if (!strncmpi(&line
[1], "passage ",
3611 sizeof "passage " - 1)) {
3613 char *st
= &line
[9]; /* 9 from "%passage " */
3616 passagenum
= atoi(st
);
3617 if (passagenum
> 0 && passagenum
<= passagecnt
) {
3618 scope
= PASSAGESCOPE
;
3619 if (matchedtitle
&& passagenum
== targetpassage
) {
3620 foundpassage
= TRUE
;
3622 tribwin
= create_nhwindow(NHW_MENU
);
3623 if (tribwin
== WIN_ERR
)
3628 } else if (!strncmpi(&line
[1], "e ", sizeof "e " - 1)) {
3631 if (scope
== TITLESCOPE
)
3632 matchedtitle
= FALSE
;
3633 if (scope
== SECTIONSCOPE
)
3634 matchedsection
= FALSE
;
3638 debugpline1("tribute file error: bad %% command, line %d.",
3643 /* comment only, next! */
3648 /* outputting multi-line passage to text window */
3649 putstr(tribwin
, 0, line
);
3651 Strcpy(lastline
, line
);
3653 /* fetching one-line passage into buffer */
3654 copynchars(nowin_buf
, line
, bufsz
- 1);
3655 goto cleanup
; /* don't wait for "%e passage" */
3662 (void) dlb_fclose(fp
);
3664 /* one-line buffer */
3665 grasped
= *nowin_buf
? TRUE
: FALSE
;
3667 if (tribwin
!= WIN_ERR
) { /* implies 'foundpassage' */
3668 /* multi-line window, normal case;
3669 if lastline is empty, there were no non-empty lines between
3670 "%passage n" and "%e passage" so we leave 'grasped' False */
3672 display_nhwindow(tribwin
, FALSE
);
3673 /* put the final attribution line into message history,
3674 analogous to the summary line from long quest messages */
3675 if (index(lastline
, '['))
3676 mungspaces(lastline
); /* to remove leading spaces */
3677 else /* construct one if necessary */
3678 Sprintf(lastline
, "[%s, by Terry Pratchett]", tribtitle
);
3679 putmsghistory(lastline
, FALSE
);
3682 destroy_nhwindow(tribwin
);
3685 /* multi-line window, problem */
3686 pline("It seems to be %s of \"%s\"!", badtranslation
, tribtitle
);
3692 Death_quote(buf
, bufsz
)
3696 unsigned death_oid
= 1; /* chance of oid #1 being a novel is negligible */
3698 return read_tribute("Death", "Death Quotes", 0, buf
, bufsz
, death_oid
);
3701 /* ---------- END TRIBUTE ----------- */