1 /* NetHack 3.6 files.c $NHDT-Date: 1459987580 2016/04/07 00:06:20 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.205 $ */
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
void FDECL(set_configfile_name
, (const char *));
192 STATIC_DCL
FILE *FDECL(fopen_config_file
, (const char *, int));
193 STATIC_DCL
int FDECL(get_uchars
, (FILE *, char *, char *, uchar
*, BOOLEAN_P
,
195 int FDECL(parse_config_line
, (FILE *, char *, int));
196 STATIC_DCL
FILE *NDECL(fopen_sym_file
);
197 STATIC_DCL
void FDECL(set_symhandling
, (char *, int));
198 #ifdef NOCWD_ASSUMPTIONS
199 STATIC_DCL
void FDECL(adjust_prefix
, (char *, int));
202 STATIC_DCL boolean
FDECL(copy_bytes
, (int, int));
204 #ifdef HOLD_LOCKFILE_OPEN
205 STATIC_DCL
int FDECL(open_levelfile_exclusively
, (const char *, int, int));
212 * legal zero-terminated list of acceptable file name characters
213 * quotechar lead-in character used to quote illegal characters as
216 * callerbuf buffer to house result
217 * bufsz size of callerbuf
220 * The hex digits 0-9 and A-F are always part of the legal set due to
221 * their use in the encoding scheme, even if not explicitly included in
225 * The following call:
226 * (void)fname_encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
227 * '%', "This is a % test!", buf, 512);
228 * results in this encoding:
229 * "This%20is%20a%20%25%20test%21"
232 fname_encode(legal
, quotechar
, s
, callerbuf
, bufsz
)
240 static char hexdigits
[] = "0123456789ABCDEF";
247 /* Do we have room for one more character or encoding? */
248 if ((bufsz
- cnt
) <= 4)
251 if (*sp
== quotechar
) {
252 (void) sprintf(op
, "%c%02X", quotechar
, *sp
);
255 } else if ((index(legal
, *sp
) != 0) || (index(hexdigits
, *sp
) != 0)) {
260 (void) sprintf(op
, "%c%02X", quotechar
, *sp
);
273 * quotechar lead-in character used to quote illegal characters as
276 * callerbuf buffer to house result
277 * bufsz size of callerbuf
280 fname_decode(quotechar
, s
, callerbuf
, bufsz
)
286 int k
, calc
, cnt
= 0;
287 static char hexdigits
[] = "0123456789ABCDEF";
295 /* Do we have room for one more character? */
296 if ((bufsz
- cnt
) <= 2)
298 if (*sp
== quotechar
) {
300 for (k
= 0; k
< 16; ++k
)
301 if (*sp
== hexdigits
[k
])
304 return callerbuf
; /* impossible, so bail */
307 for (k
= 0; k
< 16; ++k
)
308 if (*sp
== hexdigits
[k
])
311 return callerbuf
; /* impossible, so bail */
325 #ifdef PREFIXES_IN_USE
326 #define UNUSED_if_not_PREFIXES_IN_USE /*empty*/
328 #define UNUSED_if_not_PREFIXES_IN_USE UNUSED
333 fqname(basenam
, whichprefix
, buffnum
)
335 int whichprefix UNUSED_if_not_PREFIXES_IN_USE
;
336 int buffnum UNUSED_if_not_PREFIXES_IN_USE
;
338 #ifndef PREFIXES_IN_USE
341 if (!basenam
|| whichprefix
< 0 || whichprefix
>= PREFIX_COUNT
)
343 if (!fqn_prefix
[whichprefix
])
345 if (buffnum
< 0 || buffnum
>= FQN_NUMBUF
) {
346 impossible("Invalid fqn_filename_buffer specified: %d", buffnum
);
349 if (strlen(fqn_prefix
[whichprefix
]) + strlen(basenam
)
350 >= FQN_MAX_FILENAME
) {
351 impossible("fqname too long: %s + %s", fqn_prefix
[whichprefix
],
353 return basenam
; /* XXX */
355 Strcpy(fqn_filename_buffer
[buffnum
], fqn_prefix
[whichprefix
]);
356 return strcat(fqn_filename_buffer
[buffnum
], basenam
);
361 validate_prefix_locations(reasonbuf
)
362 char *reasonbuf
; /* reasonbuf must be at least BUFSZ, supplied by caller */
364 #if defined(NOCWD_ASSUMPTIONS)
366 const char *filename
;
367 int prefcnt
, failcount
= 0;
368 char panicbuf1
[BUFSZ
], panicbuf2
[BUFSZ
];
374 #if defined(NOCWD_ASSUMPTIONS)
375 for (prefcnt
= 1; prefcnt
< PREFIX_COUNT
; prefcnt
++) {
376 /* don't test writing to configdir or datadir; they're readonly */
377 if (prefcnt
== SYSCONFPREFIX
|| prefcnt
== CONFIGPREFIX
378 || prefcnt
== DATAPREFIX
)
380 filename
= fqname("validate", prefcnt
, 3);
381 if ((fp
= fopen(filename
, "w"))) {
383 (void) unlink(filename
);
387 Strcat(reasonbuf
, ", ");
388 Strcat(reasonbuf
, fqn_prefix_names
[prefcnt
]);
390 /* the paniclog entry gets the value of errno as well */
391 Sprintf(panicbuf1
, "Invalid %s", fqn_prefix_names
[prefcnt
]);
392 #if defined(NHSTDC) && !defined(NOTSTDC)
393 if (!(details
= strerror(errno
)))
396 Sprintf(panicbuf2
, "\"%s\", (%d) %s", fqn_prefix
[prefcnt
], errno
,
398 paniclog(panicbuf1
, panicbuf2
);
409 /* fopen a file, with OS-dependent bells and whistles */
410 /* NOTE: a simpler version of this routine also exists in util/dlb_main.c */
412 fopen_datafile(filename
, mode
, prefix
)
413 const char *filename
, *mode
;
418 filename
= fqname(filename
, prefix
, prefix
== TROUBLEPREFIX
? 3 : 0);
419 fp
= fopen(filename
, mode
);
423 /* ---------- BEGIN LEVEL FILE HANDLING ----------- */
426 /* Set names for bones[] and lock[] */
431 Strcpy(levels
, permbones
);
432 Strcpy(bones
, permbones
);
434 append_slash(permbones
);
435 append_slash(levels
);
437 strncat(levels
, bbs_id
, PATHLEN
);
440 Strcat(bones
, "bonesnn.*");
441 Strcpy(lock
, levels
);
443 Strcat(lock
, alllevels
);
449 /* Construct a file name for a level-type file, which is of the form
450 * something.level (with any old level stripped off).
451 * This assumes there is space on the end of 'file' to append
452 * a two digit number. This is true for 'level'
453 * but be careful if you use it for other things -dgk
456 set_levelfile_name(file
, lev
)
462 tf
= rindex(file
, '.');
465 Sprintf(tf
, ".%d", lev
);
473 create_levelfile(lev
, errbuf
)
482 set_levelfile_name(lock
, lev
);
483 fq_lock
= fqname(lock
, LEVELPREFIX
, 0);
485 #if defined(MICRO) || defined(WIN32)
486 /* Use O_TRUNC to force the file to be shortened if it already
487 * exists and is currently longer.
489 #ifdef HOLD_LOCKFILE_OPEN
491 fd
= open_levelfile_exclusively(
492 fq_lock
, lev
, O_WRONLY
| O_CREAT
| O_TRUNC
| O_BINARY
);
495 fd
= open(fq_lock
, O_WRONLY
| O_CREAT
| O_TRUNC
| O_BINARY
, FCMASK
);
498 fd
= maccreat(fq_lock
, LEVL_TYPE
);
500 fd
= creat(fq_lock
, FCMASK
);
502 #endif /* MICRO || WIN32 */
505 level_info
[lev
].flags
|= LFILE_EXISTS
;
506 else if (errbuf
) /* failure explanation */
507 Sprintf(errbuf
, "Cannot create file \"%s\" for level %d (errno %d).",
514 open_levelfile(lev
, errbuf
)
523 set_levelfile_name(lock
, lev
);
524 fq_lock
= fqname(lock
, LEVELPREFIX
, 0);
526 /* If not currently accessible, swap it in. */
527 if (level_info
[lev
].where
!= ACTIVE
)
531 fd
= macopen(fq_lock
, O_RDONLY
| O_BINARY
, LEVL_TYPE
);
533 #ifdef HOLD_LOCKFILE_OPEN
535 fd
= open_levelfile_exclusively(fq_lock
, lev
, O_RDONLY
| O_BINARY
);
538 fd
= open(fq_lock
, O_RDONLY
| O_BINARY
, 0);
541 /* for failure, return an explanation that our caller can use;
542 settle for `lock' instead of `fq_lock' because the latter
543 might end up being too big for nethack's BUFSZ */
544 if (fd
< 0 && errbuf
)
545 Sprintf(errbuf
, "Cannot open file \"%s\" for level %d (errno %d).",
552 delete_levelfile(lev
)
556 * Level 0 might be created by port specific code that doesn't
557 * call create_levfile(), so always assume that it exists.
559 if (lev
== 0 || (level_info
[lev
].flags
& LFILE_EXISTS
)) {
560 set_levelfile_name(lock
, lev
);
561 #ifdef HOLD_LOCKFILE_OPEN
565 (void) unlink(fqname(lock
, LEVELPREFIX
, 0));
566 level_info
[lev
].flags
&= ~LFILE_EXISTS
;
573 #ifdef HANGUPHANDLING
574 if (program_state
.preserve_locks
)
577 #if !defined(PC_LOCKING) && defined(MFLOPPY) && !defined(AMIGA)
578 eraseall(levels
, alllevels
);
580 eraseall(permbones
, alllevels
);
586 (void) signal(SIGINT
, SIG_IGN
);
588 #if defined(UNIX) || defined(VMS)
589 sethanguphandler((void FDECL((*), (int) )) SIG_IGN
);
591 /* can't access maxledgerno() before dungeons are created -dlc */
592 for (x
= (n_dgns
? maxledgerno() : 0); x
>= 0; x
--)
593 delete_levelfile(x
); /* not all levels need be present */
595 #endif /* ?PC_LOCKING,&c */
598 #if defined(SELECTSAVED)
599 /* qsort comparison routine */
600 STATIC_OVL
int CFDECLSPEC
605 #if defined(UNIX) && defined(QT_GRAPHICS)
606 return strncasecmp(*(char **) p
, *(char **) q
, 16);
608 return strncmpi(*(char **) p
, *(char **) q
, 16);
613 #ifdef HOLD_LOCKFILE_OPEN
615 open_levelfile_exclusively(name
, lev
, oflag
)
624 if (lftrack
.fd
>= 0) {
625 /* check for compatible access */
626 if (lftrack
.oflag
== oflag
) {
628 reslt
= lseek(fd
, 0L, SEEK_SET
);
630 panic("open_levelfile_exclusively: lseek failed %d", errno
);
631 lftrack
.nethack_thinks_it_is_open
= TRUE
;
634 fd
= sopen(name
, oflag
, SH_DENYRW
, FCMASK
);
636 lftrack
.oflag
= oflag
;
637 lftrack
.nethack_thinks_it_is_open
= TRUE
;
640 fd
= sopen(name
, oflag
, SH_DENYRW
, FCMASK
);
642 lftrack
.oflag
= oflag
;
644 lftrack
.nethack_thinks_it_is_open
= TRUE
;
654 lftrack
.nethack_thinks_it_is_open
= FALSE
;
666 if (lftrack
.fd
== fd
) {
667 really_close(); /* close it, but reopen it to hold it */
668 fd
= open_levelfile(0, (char *) 0);
669 lftrack
.nethack_thinks_it_is_open
= FALSE
;
684 /* ---------- END LEVEL FILE HANDLING ----------- */
686 /* ---------- BEGIN BONES FILE HANDLING ----------- */
688 /* set up "file" to be file name for retrieving bones, and return a
689 * bonesid to be read/written in the bones file.
692 set_bonesfile_name(file
, lev
)
699 Sprintf(file
, "bon%c%s", dungeons
[lev
->dnum
].boneid
,
700 In_quest(lev
) ? urole
.filecode
: "0");
702 if ((sptr
= Is_special(lev
)) != 0)
703 Sprintf(dptr
, ".%c", sptr
->boneid
);
705 Sprintf(dptr
, ".%d", lev
->dlevel
);
712 /* set up temporary file name for writing bones, to avoid another game's
713 * trying to read from an uncompleted bones file. we want an uncontentious
714 * name, so use one in the namespace reserved for this game's level files.
715 * (we are not reading or writing level files while writing bones files, so
716 * the same array may be used instead of copying.)
723 tf
= rindex(lock
, '.');
734 create_bonesfile(lev
, bonesid
, errbuf
)
744 *bonesid
= set_bonesfile_name(bones
, lev
);
745 file
= set_bonestemp_name();
746 file
= fqname(file
, BONESPREFIX
, 0);
748 #if defined(MICRO) || defined(WIN32)
749 /* Use O_TRUNC to force the file to be shortened if it already
750 * exists and is currently longer.
752 fd
= open(file
, O_WRONLY
| O_CREAT
| O_TRUNC
| O_BINARY
, FCMASK
);
755 fd
= maccreat(file
, BONE_TYPE
);
757 fd
= creat(file
, FCMASK
);
760 if (fd
< 0 && errbuf
) /* failure explanation */
761 Sprintf(errbuf
, "Cannot create bones \"%s\", id %s (errno %d).", lock
,
764 #if defined(VMS) && !defined(SECURE)
766 Re-protect bones file with world:read+write+execute+delete access.
767 umask() doesn't seem very reliable; also, vaxcrtl won't let us set
768 delete access without write access, which is what's really wanted.
769 Can't simply create it with the desired protection because creat
770 ANDs the mask with the user's default protection, which usually
771 denies some or all access to world.
773 (void) chmod(file
, FCMASK
| 007); /* allow other users full access */
774 #endif /* VMS && !SECURE */
780 /* remove partial bonesfile in process of creation */
784 const char *tempname
;
786 tempname
= set_bonestemp_name();
787 tempname
= fqname(tempname
, BONESPREFIX
, 0);
788 (void) unlink(tempname
);
792 /* move completed bones file to proper name */
794 commit_bonesfile(lev
)
797 const char *fq_bones
, *tempname
;
800 (void) set_bonesfile_name(bones
, lev
);
801 fq_bones
= fqname(bones
, BONESPREFIX
, 0);
802 tempname
= set_bonestemp_name();
803 tempname
= fqname(tempname
, BONESPREFIX
, 1);
805 #if (defined(SYSV) && !defined(SVR4)) || defined(GENIX)
806 /* old SYSVs don't have rename. Some SVR3's may, but since they
807 * also have link/unlink, it doesn't matter. :-)
809 (void) unlink(fq_bones
);
810 ret
= link(tempname
, fq_bones
);
811 ret
+= unlink(tempname
);
813 ret
= rename(tempname
, fq_bones
);
815 if (wizard
&& ret
!= 0)
816 pline("couldn't rename %s to %s.", tempname
, fq_bones
);
820 open_bonesfile(lev
, bonesid
)
824 const char *fq_bones
;
827 *bonesid
= set_bonesfile_name(bones
, lev
);
828 fq_bones
= fqname(bones
, BONESPREFIX
, 0);
829 nh_uncompress(fq_bones
); /* no effect if nonexistent */
831 fd
= macopen(fq_bones
, O_RDONLY
| O_BINARY
, BONE_TYPE
);
833 fd
= open(fq_bones
, O_RDONLY
| O_BINARY
, 0);
839 delete_bonesfile(lev
)
842 (void) set_bonesfile_name(bones
, lev
);
843 return !(unlink(fqname(bones
, BONESPREFIX
, 0)) < 0);
846 /* assume we're compressing the recently read or created bonesfile, so the
847 * file name is already set properly */
851 nh_compress(fqname(bones
, BONESPREFIX
, 0));
854 /* ---------- END BONES FILE HANDLING ----------- */
856 /* ---------- BEGIN SAVE FILE HANDLING ----------- */
858 /* set savefile name in OS-dependent manner from pre-existing plname,
859 * avoiding troublesome characters */
861 set_savefile_name(regularize_it
)
862 boolean regularize_it
;
865 Sprintf(SAVEF
, "[.save]%d%s", getuid(), plname
);
867 regularize(SAVEF
+ 7);
871 Strcpy(SAVEF
, SAVEP
);
873 strncat(SAVEF
, bbs_id
, PATHLEN
);
876 int i
= strlen(SAVEP
);
878 /* plname has to share space with SAVEP and ".sav" */
879 (void) strncat(SAVEF
, plname
, FILENAME
- i
- 4);
881 (void) strncat(SAVEF
, plname
, 8);
884 regularize(SAVEF
+ i
);
886 Strcat(SAVEF
, SAVE_EXTENSION
);
890 static const char okchars
[] =
891 "*ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.";
892 const char *legal
= okchars
;
893 char fnamebuf
[BUFSZ
], encodedfnamebuf
[BUFSZ
];
895 /* Obtain the name of the logged on user and incorporate
896 * it into the name. */
897 Sprintf(fnamebuf
, "%s-%s", get_username(0), plname
);
899 ++legal
; /* skip '*' wildcard character */
900 (void) fname_encode(legal
, '%', fnamebuf
, encodedfnamebuf
, BUFSZ
);
901 Sprintf(SAVEF
, "%s%s", encodedfnamebuf
, SAVE_EXTENSION
);
903 #else /* not VMS or MICRO or WIN32 */
904 Sprintf(SAVEF
, "save/%d%s", (int) getuid(), plname
);
906 regularize(SAVEF
+ 5); /* avoid . or / in name */
914 save_savefile_name(fd
)
917 (void) write(fd
, (genericptr_t
) SAVEF
, sizeof(SAVEF
));
922 /* change pre-existing savefile name to indicate an error savefile */
928 char *semi_colon
= rindex(SAVEF
, ';');
933 Strcat(SAVEF
, ".e;1");
944 /* create save file, overwriting one if it already exists */
951 fq_save
= fqname(SAVEF
, SAVEPREFIX
, 0);
952 #if defined(MICRO) || defined(WIN32)
953 fd
= open(fq_save
, O_WRONLY
| O_BINARY
| O_CREAT
| O_TRUNC
, FCMASK
);
956 fd
= maccreat(fq_save
, SAVE_TYPE
);
958 fd
= creat(fq_save
, FCMASK
);
960 #if defined(VMS) && !defined(SECURE)
962 Make sure the save file is owned by the current process. That's
963 the default for non-privileged users, but for priv'd users the
964 file will be owned by the directory's owner instead of the user.
967 (void) chown(fq_save
, getuid(), getgid());
968 #define getuid() vms_getuid()
969 #endif /* VMS && !SECURE */
975 /* open savefile for reading */
982 fq_save
= fqname(SAVEF
, SAVEPREFIX
, 0);
984 fd
= macopen(fq_save
, O_RDONLY
| O_BINARY
, SAVE_TYPE
);
986 fd
= open(fq_save
, O_RDONLY
| O_BINARY
, 0);
991 /* delete savefile */
995 (void) unlink(fqname(SAVEF
, SAVEPREFIX
, 0));
996 return 0; /* for restore_saved_game() (ex-xxxmain.c) test */
999 /* try to open up a save file and prepare to restore it */
1001 restore_saved_game()
1003 const char *fq_save
;
1007 set_savefile_name(TRUE
);
1009 if (!saveDiskPrompt(1))
1011 #endif /* MFLOPPY */
1012 fq_save
= fqname(SAVEF
, SAVEPREFIX
, 0);
1014 nh_uncompress(fq_save
);
1015 if ((fd
= open_savefile()) < 0)
1018 if (validate(fd
, fq_save
) != 0) {
1019 (void) nhclose(fd
), fd
= -1;
1020 (void) delete_savefile();
1025 #if defined(SELECTSAVED)
1027 plname_from_file(filename
)
1028 const char *filename
;
1033 Strcpy(SAVEF
, filename
);
1034 #ifdef COMPRESS_EXTENSION
1035 SAVEF
[strlen(SAVEF
) - strlen(COMPRESS_EXTENSION
)] = '\0';
1037 nh_uncompress(SAVEF
);
1038 if ((fd
= open_savefile()) >= 0) {
1039 if (validate(fd
, filename
) == 0) {
1040 char tplname
[PL_NSIZ
];
1041 get_plname_from_file(fd
, tplname
);
1042 result
= dupstr(tplname
);
1050 /* --------- obsolete - used to be ifndef STORE_PLNAME_IN_FILE ----*/
1051 #if defined(UNIX) && defined(QT_GRAPHICS)
1052 /* Name not stored in save file, so we have to extract it from
1053 the filename, which loses information
1054 (eg. "/", "_", and "." characters are lost. */
1057 char name
[64]; /* more than PL_NSIZ */
1058 #ifdef COMPRESS_EXTENSION
1059 #define EXTSTR COMPRESS_EXTENSION
1064 if ( sscanf( filename
, "%*[^/]/%d%63[^.]" EXTSTR
, &uid
, name
) == 2 ) {
1066 /* "_" most likely means " ", which certainly looks nicer */
1067 for (k
=0; name
[k
]; k
++)
1068 if ( name
[k
] == '_' )
1070 return dupstr(name
);
1072 #endif /* UNIX && QT_GRAPHICS */
1076 /* --------- end of obsolete code ----*/
1077 #endif /* 0 - WAS STORE_PLNAME_IN_FILE*/
1079 #endif /* defined(SELECTSAVED) */
1084 #if defined(SELECTSAVED)
1090 const char *fq_save
;
1092 Strcpy(plname
, "*");
1093 set_savefile_name(FALSE
);
1094 #if defined(ZLIB_COMP)
1095 Strcat(SAVEF
, COMPRESS_EXTENSION
);
1097 fq_save
= fqname(SAVEF
, SAVEPREFIX
, 0);
1100 foundfile
= foundfile_buffer();
1101 if (findfirst((char *) fq_save
)) {
1104 } while (findnext());
1107 result
= (char **) alloc((n
+ 1) * sizeof(char *)); /* at most */
1108 (void) memset((genericptr_t
) result
, 0, (n
+ 1) * sizeof(char *));
1109 if (findfirst((char *) fq_save
)) {
1113 r
= plname_from_file(foundfile
);
1117 } while (findnext());
1122 #if defined(UNIX) && defined(QT_GRAPHICS)
1123 /* posixly correct version */
1124 int myuid
= getuid();
1127 if ((dir
= opendir(fqname("save", SAVEPREFIX
, 0)))) {
1128 for (n
= 0; readdir(dir
); n
++)
1134 if (!(dir
= opendir(fqname("save", SAVEPREFIX
, 0))))
1136 result
= (char **) alloc((n
+ 1) * sizeof(char *)); /* at most */
1137 (void) memset((genericptr_t
) result
, 0, (n
+ 1) * sizeof(char *));
1138 for (i
= 0, j
= 0; i
< n
; i
++) {
1140 char name
[64]; /* more than PL_NSIZ */
1141 struct dirent
*entry
= readdir(dir
);
1145 if (sscanf(entry
->d_name
, "%d%63s", &uid
, name
) == 2) {
1147 char filename
[BUFSZ
];
1150 Sprintf(filename
, "save/%d%s", uid
, name
);
1151 r
= plname_from_file(filename
);
1162 Strcpy(plname
, "*");
1163 set_savefile_name(FALSE
);
1164 j
= vms_get_saved_games(SAVEF
, &result
);
1169 qsort(result
, j
, sizeof (char *), strcmp_wrap
);
1172 } else if (result
) { /* could happen if save files are obsolete */
1173 free_saved_games(result
);
1175 #endif /* SELECTSAVED */
1180 free_saved_games(saved
)
1187 free((genericptr_t
) saved
[i
++]);
1188 free((genericptr_t
) saved
);
1192 /* ---------- END SAVE FILE HANDLING ----------- */
1194 /* ---------- BEGIN FILE COMPRESSION HANDLING ----------- */
1199 redirect(filename
, mode
, stream
, uncomp
)
1200 const char *filename
, *mode
;
1204 if (freopen(filename
, mode
, stream
) == (FILE *) 0) {
1205 (void) fprintf(stderr
, "freopen of %s for %scompress failed\n",
1206 filename
, uncomp
? "un" : "");
1207 terminate(EXIT_FAILURE
);
1212 * using system() is simpler, but opens up security holes and causes
1213 * problems on at least Interactive UNIX 3.0.1 (SVR3.2), where any
1214 * setuid is renounced by /bin/sh, so the files cannot be accessed.
1216 * cf. child() in unixunix.c.
1219 docompress_file(filename
, uncomp
)
1220 const char *filename
;
1225 const char *args
[10];
1226 #ifdef COMPRESS_OPTIONS
1232 boolean istty
= !strncmpi(windowprocs
.name
, "tty", 3);
1235 Strcpy(cfn
, filename
);
1236 #ifdef COMPRESS_EXTENSION
1237 Strcat(cfn
, COMPRESS_EXTENSION
);
1239 /* when compressing, we know the file exists */
1241 if ((cf
= fopen(cfn
, RDBMODE
)) == (FILE *) 0)
1248 args
[++i
] = "-d"; /* uncompress */
1249 #ifdef COMPRESS_OPTIONS
1251 /* we can't guarantee there's only one additional option, sigh */
1253 boolean inword
= FALSE
;
1255 Strcpy(opts
, COMPRESS_OPTIONS
);
1258 if ((*opt
== ' ') || (*opt
== '\t')) {
1263 } else if (!inword
) {
1271 args
[++i
] = (char *) 0;
1274 /* If we don't do this and we are right after a y/n question *and*
1275 * there is an error message from the compression, the 'y' or 'n' can
1276 * end up being displayed after the error message.
1282 if (f
== 0) { /* child */
1284 /* any error messages from the compression must come out after
1285 * the first line, because the more() to let the user read
1286 * them will have to clear the first line. This should be
1287 * invisible if there are no error messages.
1292 /* run compressor without privileges, in case other programs
1293 * have surprises along the line of gzip once taking filenames
1296 /* assume all compressors will compress stdin to stdout
1297 * without explicit filenames. this is true of at least
1298 * compress and gzip, those mentioned in config.h.
1301 redirect(cfn
, RDBMODE
, stdin
, uncomp
);
1302 redirect(filename
, WRBMODE
, stdout
, uncomp
);
1304 redirect(filename
, RDBMODE
, stdin
, uncomp
);
1305 redirect(cfn
, WRBMODE
, stdout
, uncomp
);
1307 (void) setgid(getgid());
1308 (void) setuid(getuid());
1309 (void) execv(args
[0], (char *const *) args
);
1311 (void) fprintf(stderr
, "Exec to %scompress %s failed.\n",
1312 uncomp
? "un" : "", filename
);
1313 terminate(EXIT_FAILURE
);
1314 } else if (f
== -1) {
1316 pline("Fork to %scompress %s failed.", uncomp
? "un" : "", filename
);
1320 (void) signal(SIGINT
, SIG_IGN
);
1321 (void) signal(SIGQUIT
, SIG_IGN
);
1322 (void) wait((int *) &i
);
1323 (void) signal(SIGINT
, (SIG_RET_TYPE
) done1
);
1325 (void) signal(SIGQUIT
, SIG_DFL
);
1327 /* I don't think we can really cope with external compression
1328 * without signals, so we'll declare that compress failed and
1329 * go on. (We could do a better job by forcing off external
1330 * compression if there are no signals, but we want this for
1331 * testing with FailSafeC
1336 /* (un)compress succeeded: remove file left behind */
1340 (void) unlink(filename
);
1342 /* (un)compress failed; remove the new, bad file */
1344 raw_printf("Unable to uncompress %s", filename
);
1345 (void) unlink(filename
);
1347 /* no message needed for compress case; life will go on */
1351 /* Give them a chance to read any error messages from the
1352 * compression--these would go to stdout or stderr and would get
1353 * overwritten only in tty mode. It's still ugly, since the
1354 * messages are being written on top of the screen, but at least
1355 * the user can read them.
1357 if (istty
&& iflags
.window_inited
) {
1358 clear_nhwindow(WIN_MESSAGE
);
1360 /* No way to know if this is feasible */
1366 #endif /* COMPRESS */
1368 #if defined(COMPRESS) || defined(ZLIB_COMP)
1369 #define UNUSED_if_not_COMPRESS /*empty*/
1371 #define UNUSED_if_not_COMPRESS UNUSED
1376 nh_compress(filename
)
1377 const char *filename UNUSED_if_not_COMPRESS
;
1379 #if !defined(COMPRESS) && !defined(ZLIB_COMP)
1380 #ifdef PRAGMA_UNUSED
1381 #pragma unused(filename)
1384 docompress_file(filename
, FALSE
);
1388 /* uncompress file if it exists */
1390 nh_uncompress(filename
)
1391 const char *filename UNUSED_if_not_COMPRESS
;
1393 #if !defined(COMPRESS) && !defined(ZLIB_COMP)
1394 #ifdef PRAGMA_UNUSED
1395 #pragma unused(filename)
1398 docompress_file(filename
, TRUE
);
1402 #ifdef ZLIB_COMP /* RLC 09 Mar 1999: Support internal ZLIB */
1404 make_compressed_name(filename
, cfn
)
1405 const char *filename
;
1408 #ifndef SHORT_FILENAMES
1409 /* Assume free-form filename with no 8.3 restrictions */
1410 strcpy(cfn
, filename
);
1411 strcat(cfn
, COMPRESS_EXTENSION
);
1414 #ifdef SAVE_EXTENSION
1415 char *bp
= (char *) 0;
1417 strcpy(cfn
, filename
);
1418 if ((bp
= strstri(cfn
, SAVE_EXTENSION
))) {
1419 strsubst(bp
, SAVE_EXTENSION
, ".saz");
1422 /* find last occurrence of bon */
1424 while (bp
-- > cfn
) {
1425 if (strstri(bp
, "bon")) {
1426 strsubst(bp
, "bon", "boz");
1431 #endif /* SAVE_EXTENSION */
1433 #endif /* SHORT_FILENAMES */
1437 docompress_file(filename
, uncomp
)
1438 const char *filename
;
1441 gzFile compressedfile
;
1442 FILE *uncompressedfile
;
1447 if (!make_compressed_name(filename
, cfn
))
1451 /* Open the input and output files */
1452 /* Note that gzopen takes "wb" as its mode, even on systems where
1453 fopen takes "r" and "w" */
1455 uncompressedfile
= fopen(filename
, RDBMODE
);
1456 if (!uncompressedfile
) {
1457 pline("Error in zlib docompress_file %s", filename
);
1460 compressedfile
= gzopen(cfn
, "wb");
1461 if (compressedfile
== NULL
) {
1463 pline("zlib failed to allocate memory");
1465 panic("Error in docompress_file %d", errno
);
1467 fclose(uncompressedfile
);
1471 /* Copy from the uncompressed to the compressed file */
1474 len
= fread(buf
, 1, sizeof(buf
), uncompressedfile
);
1475 if (ferror(uncompressedfile
)) {
1476 pline("Failure reading uncompressed file");
1477 pline("Can't compress %s.", filename
);
1478 fclose(uncompressedfile
);
1479 gzclose(compressedfile
);
1484 break; /* End of file */
1486 len2
= gzwrite(compressedfile
, buf
, len
);
1488 pline("Failure writing compressed file");
1489 pline("Can't compress %s.", filename
);
1490 fclose(uncompressedfile
);
1491 gzclose(compressedfile
);
1497 fclose(uncompressedfile
);
1498 gzclose(compressedfile
);
1500 /* Delete the file left behind */
1502 (void) unlink(filename
);
1504 } else { /* uncomp */
1506 /* Open the input and output files */
1507 /* Note that gzopen takes "rb" as its mode, even on systems where
1508 fopen takes "r" and "w" */
1510 compressedfile
= gzopen(cfn
, "rb");
1511 if (compressedfile
== NULL
) {
1513 pline("zlib failed to allocate memory");
1514 } else if (errno
!= ENOENT
) {
1515 panic("Error in zlib docompress_file %s, %d", filename
,
1520 uncompressedfile
= fopen(filename
, WRBMODE
);
1521 if (!uncompressedfile
) {
1522 pline("Error in zlib docompress file uncompress %s", filename
);
1523 gzclose(compressedfile
);
1527 /* Copy from the compressed to the uncompressed file */
1530 len
= gzread(compressedfile
, buf
, sizeof(buf
));
1531 if (len
== (unsigned) -1) {
1532 pline("Failure reading compressed file");
1533 pline("Can't uncompress %s.", filename
);
1534 fclose(uncompressedfile
);
1535 gzclose(compressedfile
);
1536 (void) unlink(filename
);
1540 break; /* End of file */
1542 fwrite(buf
, 1, len
, uncompressedfile
);
1543 if (ferror(uncompressedfile
)) {
1544 pline("Failure writing uncompressed file");
1545 pline("Can't uncompress %s.", filename
);
1546 fclose(uncompressedfile
);
1547 gzclose(compressedfile
);
1548 (void) unlink(filename
);
1553 fclose(uncompressedfile
);
1554 gzclose(compressedfile
);
1556 /* Delete the file left behind */
1560 #endif /* RLC 09 Mar 1999: End ZLIB patch */
1562 /* ---------- END FILE COMPRESSION HANDLING ----------- */
1564 /* ---------- BEGIN FILE LOCKING HANDLING ----------- */
1566 static int nesting
= 0;
1568 #if defined(NO_FILE_LINKS) || defined(USE_FCNTL) /* implies UNIX */
1569 static int lockfd
; /* for lock_file() to pass to unlock_file() */
1572 struct flock sflock
; /* for unlocking, same as above */
1575 #define HUP if (!program_state.done_hup)
1579 make_lockname(filename
, lockname
)
1580 const char *filename
;
1583 #if defined(UNIX) || defined(VMS) || defined(AMIGA) || defined(WIN32) \
1585 #ifdef NO_FILE_LINKS
1586 Strcpy(lockname
, LOCKDIR
);
1587 Strcat(lockname
, "/");
1588 Strcat(lockname
, filename
);
1590 Strcpy(lockname
, filename
);
1594 char *semi_colon
= rindex(lockname
, ';');
1598 Strcat(lockname
, ".lock;1");
1600 Strcat(lockname
, "_lock");
1603 #else /* !(UNIX || VMS || AMIGA || WIN32 || MSDOS) */
1604 #ifdef PRAGMA_UNUSED
1605 #pragma unused(filename)
1611 #endif /* !USE_FCNTL */
1615 lock_file(filename
, whichprefix
, retryct
)
1616 const char *filename
;
1620 #if defined(PRAGMA_UNUSED) && !(defined(UNIX) || defined(VMS)) \
1621 && !(defined(AMIGA) || defined(WIN32) || defined(MSDOS))
1622 #pragma unused(retryct)
1625 char locknambuf
[BUFSZ
];
1626 const char *lockname
;
1631 impossible("TRIED TO NEST LOCKS");
1636 lockname
= make_lockname(filename
, locknambuf
);
1637 #ifndef NO_FILE_LINKS /* LOCKDIR should be subsumed by LOCKPREFIX */
1638 lockname
= fqname(lockname
, LOCKPREFIX
, 2);
1641 filename
= fqname(filename
, whichprefix
, 0);
1643 lockfd
= open(filename
, O_RDWR
);
1645 HUP
raw_printf("Cannot open file %s. This is a program bug.",
1648 sflock
.l_type
= F_WRLCK
;
1649 sflock
.l_whence
= SEEK_SET
;
1654 #if defined(UNIX) || defined(VMS)
1656 while (fcntl(lockfd
, F_SETLK
, &sflock
) == -1) {
1658 #ifdef NO_FILE_LINKS
1659 while ((lockfd
= open(lockname
, O_RDWR
| O_CREAT
| O_EXCL
, 0666)) == -1) {
1661 while (link(filename
, lockname
) == -1) {
1668 "Waiting for release of fcntl lock on %s. (%d retries left).",
1672 HUP(void) raw_print("I give up. Sorry.");
1673 HUP
raw_printf("Some other process has an unnatural grip on %s.",
1679 register int errnosv
= errno
;
1681 switch (errnosv
) { /* George Barbanis */
1685 "Waiting for access to %s. (%d retries left).", filename
,
1687 #if defined(SYSV) || defined(ULTRIX) || defined(VMS)
1692 HUP(void) raw_print("I give up. Sorry.");
1693 HUP
raw_printf("Perhaps there is an old %s around?",
1701 HUP
raw_printf("Can't find file %s to lock!", filename
);
1705 HUP
raw_printf("No write permission to lock %s!", filename
);
1708 #ifdef VMS /* c__translate(vmsfiles.c) */
1710 /* could be misleading, but usually right */
1711 HUP
raw_printf("Can't lock %s due to directory protection.",
1717 /* take a wild guess at the underlying cause */
1718 HUP
perror(lockname
);
1719 HUP
raw_printf("Cannot lock %s.", filename
);
1721 "(Perhaps you are running NetHack from inside the distribution package?).");
1725 HUP
perror(lockname
);
1726 HUP
raw_printf("Cannot lock %s for unknown reason (%d).",
1731 #endif /* USE_FCNTL */
1733 #endif /* UNIX || VMS */
1735 #if (defined(AMIGA) || defined(WIN32) || defined(MSDOS)) \
1736 && !defined(USE_FCNTL)
1738 #define OPENFAILURE(fd) (!fd)
1741 #define OPENFAILURE(fd) (fd < 0)
1744 while (--retryct
&& OPENFAILURE(lockptr
)) {
1745 #if defined(WIN32) && !defined(WIN_CE)
1746 lockptr
= sopen(lockname
, O_RDWR
| O_CREAT
, SH_DENYRW
, S_IWRITE
);
1748 (void) DeleteFile(lockname
); /* in case dead process was here first */
1750 lockptr
= Open(lockname
, MODE_NEWFILE
);
1752 lockptr
= open(lockname
, O_RDWR
| O_CREAT
| O_EXCL
, S_IWRITE
);
1755 if (OPENFAILURE(lockptr
)) {
1756 raw_printf("Waiting for access to %s. (%d retries left).",
1762 raw_printf("I give up. Sorry.");
1766 #endif /* AMIGA || WIN32 || MSDOS */
1770 #ifdef VMS /* for unlock_file, use the unlink() routine in vmsunix.c */
1774 #define unlink(foo) vms_unlink(foo)
1777 /* unlock file, which must be currently locked by lock_file */
1779 unlock_file(filename
)
1780 const char *filename
;
1783 char locknambuf
[BUFSZ
];
1784 const char *lockname
;
1789 sflock
.l_type
= F_UNLCK
;
1790 if (fcntl(lockfd
, F_SETLK
, &sflock
) == -1) {
1791 HUP
raw_printf("Can't remove fcntl lock on %s.", filename
);
1792 (void) close(lockfd
);
1795 lockname
= make_lockname(filename
, locknambuf
);
1796 #ifndef NO_FILE_LINKS /* LOCKDIR should be subsumed by LOCKPREFIX */
1797 lockname
= fqname(lockname
, LOCKPREFIX
, 2);
1800 #if defined(UNIX) || defined(VMS)
1801 if (unlink(lockname
) < 0)
1802 HUP
raw_printf("Can't unlink %s.", lockname
);
1803 #ifdef NO_FILE_LINKS
1804 (void) nhclose(lockfd
);
1807 #endif /* UNIX || VMS */
1809 #if defined(AMIGA) || defined(WIN32) || defined(MSDOS)
1812 DeleteFile(lockname
);
1814 #endif /* AMIGA || WIN32 || MSDOS */
1815 #endif /* USE_FCNTL */
1821 /* ---------- END FILE LOCKING HANDLING ----------- */
1823 /* ---------- BEGIN CONFIG FILE HANDLING ----------- */
1825 const char *default_configfile
=
1829 #if defined(MAC) || defined(__BEOS__)
1832 #if defined(MSDOS) || defined(WIN32)
1840 /* used for messaging */
1841 char configfile
[BUFSZ
];
1844 /* conflict with speed-dial under windows
1845 * for XXX.cnf file so support of NetHack.cnf
1846 * is for backward compatibility only.
1847 * Preferred name (and first tried) is now defaults.nh but
1848 * the game will try the old name if there
1849 * is no defaults.nh.
1851 const char *backward_compat_configfile
= "nethack.cnf";
1854 /* remember the name of the file we're accessing;
1855 if may be used in option reject messages */
1857 set_configfile_name(fname
)
1860 (void) strncpy(configfile
, fname
, sizeof configfile
- 1);
1861 configfile
[sizeof configfile
- 1] = '\0';
1865 #define fopenp fopen
1869 fopen_config_file(filename
, src
)
1870 const char *filename
;
1874 #if defined(UNIX) || defined(VMS)
1875 char tmp_config
[BUFSZ
];
1879 if (src
== SET_IN_SYS
) {
1880 /* SYSCF_FILE; if we can't open it, caller will bail */
1881 if (filename
&& *filename
) {
1882 set_configfile_name(fqname(filename
, SYSCONFPREFIX
, 0));
1883 fp
= fopenp(configfile
, "r");
1888 /* If src != SET_IN_SYS, "filename" is an environment variable, so it
1889 * should hang around. If set, it is expected to be a full path name
1892 if (filename
&& *filename
) {
1893 set_configfile_name(filename
);
1895 if (access(configfile
, 4) == -1) { /* 4 is R_OK on newer systems */
1896 /* nasty sneaky attempt to read file through
1897 * NetHack's setuid permissions -- this is the only
1898 * place a file name may be wholly under the player's
1899 * control (but SYSCF_FILE is not under the player's
1900 * control so it's OK).
1902 raw_printf("Access to %s denied (%d).", configfile
, errno
);
1904 /* fall through to standard names */
1907 if ((fp
= fopenp(configfile
, "r")) != (FILE *) 0) {
1909 #if defined(UNIX) || defined(VMS)
1911 /* access() above probably caught most problems for UNIX */
1912 raw_printf("Couldn't open requested config file %s (%d).",
1918 /* fall through to standard names */
1920 #if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32)
1921 set_configfile_name(fqname(default_configfile
, CONFIGPREFIX
, 0));
1922 if ((fp
= fopenp(configfile
, "r")) != (FILE *) 0) {
1924 } else if (strcmp(default_configfile
, configfile
)) {
1925 set_configfile_name(default_configfile
);
1926 if ((fp
= fopenp(configfile
, "r")) != (FILE *) 0)
1930 set_configfile_name(fqname(backward_compat_configfile
, CONFIGPREFIX
, 0));
1931 if ((fp
= fopenp(configfile
, "r")) != (FILE *) 0) {
1933 } else if (strcmp(backwad_compat_configfile
, configfile
)) {
1934 set_configfile_name(backward_compat_configfile
);
1935 if ((fp
= fopenp(configfile
, "r")) != (FILE *) 0)
1940 /* constructed full path names don't need fqname() */
1942 /* no punctuation, so might be a logical name */
1943 set_configfile_name(fqname("nethackini", CONFIGPREFIX
, 0));
1944 if ((fp
= fopenp(configfile
, "r")) != (FILE *) 0)
1946 set_configfile_name("sys$login:nethack.ini");
1947 if ((fp
= fopenp(configfile
, "r")) != (FILE *) 0)
1950 envp
= nh_getenv("HOME");
1951 if (!envp
|| !*envp
)
1952 Strcpy(tmp_config
, "NetHack.cnf");
1954 Sprintf(tmp_config
, "%s%s%s", envp
,
1955 !index(":]>/", envp
[strlen(envp
) - 1]) ? "/" : "",
1957 set_configfile_name(tmp_config
);
1958 if ((fp
= fopenp(configfile
, "r")) != (FILE *) 0)
1960 #else /* should be only UNIX left */
1961 envp
= nh_getenv("HOME");
1963 Strcpy(tmp_config
, ".nethackrc");
1965 Sprintf(tmp_config
, "%s/%s", envp
, ".nethackrc");
1967 set_configfile_name(tmp_config
);
1968 if ((fp
= fopenp(configfile
, "r")) != (FILE *) 0)
1970 #if defined(__APPLE__) /* UNIX+__APPLE__ => MacOSX */
1971 /* try an alternative */
1973 /* OSX-style configuration settings */
1974 Sprintf(tmp_config
, "%s/%s", envp
,
1975 "Library/Preferences/NetHack Defaults");
1976 set_configfile_name(tmp_config
);
1977 if ((fp
= fopenp(configfile
, "r")) != (FILE *) 0)
1979 /* may be easier for user to edit if filename as '.txt' suffix */
1980 Sprintf(tmp_config
, "%s/%s", envp
,
1981 "Library/Preferences/NetHack Defaults.txt");
1982 set_configfile_name(tmp_config
);
1983 if ((fp
= fopenp(configfile
, "r")) != (FILE *) 0)
1986 #endif /*__APPLE__*/
1987 if (errno
!= ENOENT
) {
1988 const char *details
;
1990 /* e.g., problems when setuid NetHack can't search home
1991 directory restricted to user */
1992 #if defined(NHSTDC) && !defined(NOTSTDC)
1993 if ((details
= strerror(errno
)) == 0)
1996 raw_printf("Couldn't open default config file %s %s(%d).",
1997 configfile
, details
, errno
);
2000 #endif /* !VMS => Unix */
2001 #endif /* !(MICRO || MAC || __BEOS__ || WIN32) */
2006 * Retrieve a list of integers from a file into a uchar array.
2008 * NOTE: zeros are inserted unless modlist is TRUE, in which case the list
2009 * location is unchanged. Callers must handle zeros if modlist is FALSE.
2012 get_uchars(fp
, buf
, bufp
, list
, modlist
, size
, name
)
2013 FILE *fp
; /* input file pointer */
2014 char *buf
; /* read buffer, must be of size BUFSZ */
2015 char *bufp
; /* current pointer */
2016 uchar
*list
; /* return list */
2017 boolean modlist
; /* TRUE: list is being modified in place */
2018 int size
; /* return list size */
2019 const char *name
; /* name of option for error message */
2021 unsigned int num
= 0;
2023 boolean havenum
= FALSE
;
2032 /* if modifying in place, don't insert zeros */
2033 if (num
|| !modlist
)
2039 if (count
== size
|| !*bufp
)
2055 num
= num
* 10 + (*bufp
- '0');
2060 if (fp
== (FILE *) 0)
2063 if (!fgets(buf
, BUFSZ
, fp
))
2065 } while (buf
[0] == '#');
2071 raw_printf("Syntax error in %s", name
);
2079 #ifdef NOCWD_ASSUMPTIONS
2081 adjust_prefix(bufp
, prefixid
)
2089 /* Backward compatibility, ignore trailing ;n */
2090 if ((ptr
= index(bufp
, ';')) != 0)
2092 if (strlen(bufp
) > 0) {
2093 fqn_prefix
[prefixid
] = (char *) alloc(strlen(bufp
) + 2);
2094 Strcpy(fqn_prefix
[prefixid
], bufp
);
2095 append_slash(fqn_prefix
[prefixid
]);
2100 #define match_varname(INP, NAM, LEN) match_optname(INP, NAM, LEN, TRUE)
2103 parse_config_line(fp
, origbuf
, src
)
2108 #if defined(MICRO) && !defined(NOCWD_ASSUMPTIONS)
2109 static boolean ramdisk_specified
= FALSE
;
2114 char *bufp
, *altp
, buf
[4 * BUFSZ
];
2115 uchar translate
[MAXPCHARS
];
2118 /* convert any tab to space, condense consecutive spaces into one,
2119 remove leading and trailing spaces (exception: if there is nothing
2120 but spaces, one of them will be kept even though it leads/trails) */
2121 mungspaces(strcpy(buf
, origbuf
));
2122 /* lines beginning with '#' are comments; accept empty lines too */
2123 if (!*buf
|| *buf
== '#' || !strcmp(buf
, " "))
2126 /* find the '=' or ':' */
2127 bufp
= index(buf
, '=');
2128 altp
= index(buf
, ':');
2129 if (!bufp
|| (altp
&& altp
< bufp
))
2133 /* skip past '=', then space between it and value, if any */
2138 /* Go through possible variables */
2139 /* some of these (at least LEVELS and SAVE) should now set the
2140 * appropriate fqn_prefix[] rather than specialized variables
2142 if (match_varname(buf
, "OPTIONS", 4)) {
2143 /* hack: un-mungspaces to allow consecutive spaces in
2144 general options until we verify that this is unnecessary;
2145 '=' or ':' is guaranteed to be present */
2146 bufp
= index(origbuf
, '=');
2147 altp
= index(origbuf
, ':');
2148 if (!bufp
|| (altp
&& altp
< bufp
))
2150 ++bufp
; /* skip '='; parseoptions() handles spaces */
2152 parseoptions(bufp
, TRUE
, TRUE
);
2153 } else if (match_varname(buf
, "AUTOPICKUP_EXCEPTION", 5)) {
2154 add_autopickup_exception(bufp
);
2155 } else if (match_varname(buf
, "BINDINGS", 4)) {
2156 parsebindings(bufp
);
2157 } else if (match_varname(buf
, "AUTOCOMPLETE", 5)) {
2158 parseautocomplete(bufp
, TRUE
);
2159 } else if (match_varname(buf
, "MSGTYPE", 7)) {
2160 (void) msgtype_parse_add(bufp
);
2161 #ifdef NOCWD_ASSUMPTIONS
2162 } else if (match_varname(buf
, "HACKDIR", 4)) {
2163 adjust_prefix(bufp
, HACKPREFIX
);
2164 } else if (match_varname(buf
, "LEVELDIR", 4)
2165 || match_varname(buf
, "LEVELS", 4)) {
2166 adjust_prefix(bufp
, LEVELPREFIX
);
2167 } else if (match_varname(buf
, "SAVEDIR", 4)) {
2168 adjust_prefix(bufp
, SAVEPREFIX
);
2169 } else if (match_varname(buf
, "BONESDIR", 5)) {
2170 adjust_prefix(bufp
, BONESPREFIX
);
2171 } else if (match_varname(buf
, "DATADIR", 4)) {
2172 adjust_prefix(bufp
, DATAPREFIX
);
2173 } else if (match_varname(buf
, "SCOREDIR", 4)) {
2174 adjust_prefix(bufp
, SCOREPREFIX
);
2175 } else if (match_varname(buf
, "LOCKDIR", 4)) {
2176 adjust_prefix(bufp
, LOCKPREFIX
);
2177 } else if (match_varname(buf
, "CONFIGDIR", 4)) {
2178 adjust_prefix(bufp
, CONFIGPREFIX
);
2179 } else if (match_varname(buf
, "TROUBLEDIR", 4)) {
2180 adjust_prefix(bufp
, TROUBLEPREFIX
);
2181 #else /*NOCWD_ASSUMPTIONS*/
2183 } else if (match_varname(buf
, "HACKDIR", 4)) {
2184 (void) strncpy(hackdir
, bufp
, PATHLEN
- 1);
2186 } else if (match_varname(buf
, "RAMDISK", 3)) {
2187 /* The following ifdef is NOT in the wrong
2188 * place. For now, we accept and silently
2191 if (strlen(bufp
) >= PATHLEN
)
2192 bufp
[PATHLEN
- 1] = '\0';
2193 Strcpy(levels
, bufp
);
2194 ramdisk
= (strcmp(permbones
, levels
) != 0);
2195 ramdisk_specified
= TRUE
;
2198 } else if (match_varname(buf
, "LEVELS", 4)) {
2199 if (strlen(bufp
) >= PATHLEN
)
2200 bufp
[PATHLEN
- 1] = '\0';
2201 Strcpy(permbones
, bufp
);
2202 if (!ramdisk_specified
|| !*levels
)
2203 Strcpy(levels
, bufp
);
2204 ramdisk
= (strcmp(permbones
, levels
) != 0);
2205 } else if (match_varname(buf
, "SAVE", 4)) {
2207 extern int saveprompt
;
2211 if ((ptr
= index(bufp
, ';')) != 0) {
2214 if (*(ptr
+ 1) == 'n' || *(ptr
+ 1) == 'N') {
2219 #if defined(SYSFLAGS) && defined(MFLOPPY)
2221 saveprompt
= sysflags
.asksavedisk
;
2224 (void) strncpy(SAVEP
, bufp
, SAVESIZE
- 1);
2225 append_slash(SAVEP
);
2227 #endif /*NOCWD_ASSUMPTIONS*/
2229 } else if (match_varname(buf
, "NAME", 4)) {
2230 (void) strncpy(plname
, bufp
, PL_NSIZ
- 1);
2231 } else if (match_varname(buf
, "ROLE", 4)
2232 || match_varname(buf
, "CHARACTER", 4)) {
2233 if ((len
= str2role(bufp
)) >= 0)
2234 flags
.initrole
= len
;
2235 } else if (match_varname(buf
, "DOGNAME", 3)) {
2236 (void) strncpy(dogname
, bufp
, PL_PSIZ
- 1);
2237 } else if (match_varname(buf
, "CATNAME", 3)) {
2238 (void) strncpy(catname
, bufp
, PL_PSIZ
- 1);
2241 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "WIZARDS", 7)) {
2243 free((genericptr_t
) sysopt
.wizards
);
2244 sysopt
.wizards
= dupstr(bufp
);
2245 if (strlen(sysopt
.wizards
) && strcmp(sysopt
.wizards
, "*")) {
2246 /* pre-format WIZARDS list now; it's displayed during a panic
2247 and since that panic might be due to running out of memory,
2248 we don't want to risk attempting to allocate any memory then */
2249 if (sysopt
.fmtd_wizard_list
)
2250 free((genericptr_t
) sysopt
.fmtd_wizard_list
);
2251 sysopt
.fmtd_wizard_list
= build_english_list(sysopt
.wizards
);
2253 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "SHELLERS", 8)) {
2254 if (sysopt
.shellers
)
2255 free((genericptr_t
) sysopt
.shellers
);
2256 sysopt
.shellers
= dupstr(bufp
);
2257 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "EXPLORERS", 7)) {
2258 if (sysopt
.explorers
)
2259 free((genericptr_t
) sysopt
.explorers
);
2260 sysopt
.explorers
= dupstr(bufp
);
2261 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "DEBUGFILES", 5)) {
2262 /* if showdebug() has already been called (perhaps we've added
2263 some debugpline() calls to option processing) and has found
2264 a value for getenv("DEBUGFILES"), don't override that */
2265 if (sysopt
.env_dbgfl
<= 0) {
2266 if (sysopt
.debugfiles
)
2267 free((genericptr_t
) sysopt
.debugfiles
);
2268 sysopt
.debugfiles
= dupstr(bufp
);
2270 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "GENERICUSERS", 12)) {
2271 if (sysopt
.genericusers
) free(sysopt
.genericusers
);
2272 sysopt
.genericusers
= dupstr(bufp
);
2273 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "SUPPORT", 7)) {
2275 free((genericptr_t
) sysopt
.support
);
2276 sysopt
.support
= dupstr(bufp
);
2277 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "RECOVER", 7)) {
2279 free((genericptr_t
) sysopt
.recover
);
2280 sysopt
.recover
= dupstr(bufp
);
2281 } else if (src
== SET_IN_SYS
2282 && match_varname(buf
, "CHECK_SAVE_UID", 14)) {
2284 sysopt
.check_save_uid
= n
;
2285 } else if (src
== SET_IN_SYS
2286 && match_varname(buf
, "CHECK_PLNAME", 12)) {
2288 sysopt
.check_plname
= n
;
2289 } else if (match_varname(buf
, "SEDUCE", 6)) {
2290 n
= !!atoi(bufp
); /* XXX this could be tighter */
2291 /* allow anyone to turn it off, but only sysconf to turn it on*/
2292 if (src
!= SET_IN_SYS
&& n
!= 0) {
2293 raw_printf("Illegal value in SEDUCE");
2297 sysopt_seduce_set(sysopt
.seduce
);
2298 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "MAXPLAYERS", 10)) {
2300 /* XXX to get more than 25, need to rewrite all lock code */
2301 if (n
< 0 || n
> 25) {
2302 raw_printf("Illegal value in MAXPLAYERS (maximum is 25).");
2305 sysopt
.maxplayers
= n
;
2306 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "PERSMAX", 7)) {
2309 raw_printf("Illegal value in PERSMAX (minimum is 1).");
2313 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "PERS_IS_UID", 11)) {
2315 if (n
!= 0 && n
!= 1) {
2316 raw_printf("Illegal value in PERS_IS_UID (must be 0 or 1).");
2319 sysopt
.pers_is_uid
= n
;
2320 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "ENTRYMAX", 8)) {
2323 raw_printf("Illegal value in ENTRYMAX (minimum is 10).");
2326 sysopt
.entrymax
= n
;
2327 } else if ((src
== SET_IN_SYS
) && match_varname(buf
, "POINTSMIN", 9)) {
2330 raw_printf("Illegal value in POINTSMIN (minimum is 1).");
2333 sysopt
.pointsmin
= n
;
2334 } else if (src
== SET_IN_SYS
2335 && match_varname(buf
, "MAX_STATUENAME_RANK", 10)) {
2339 "Illegal value in MAX_STATUENAME_RANK (minimum is 1).");
2342 sysopt
.tt_oname_maxrank
= n
;
2344 /* SYSCF PANICTRACE options */
2345 } else if (src
== SET_IN_SYS
2346 && match_varname(buf
, "PANICTRACE_LIBC", 15)) {
2348 #if defined(PANICTRACE) && defined(PANICTRACE_LIBC)
2349 if (n
< 0 || n
> 2) {
2350 raw_printf("Illegal value in PANICTRACE_LIBC (not 0,1,2).");
2354 sysopt
.panictrace_libc
= n
;
2355 } else if (src
== SET_IN_SYS
2356 && match_varname(buf
, "PANICTRACE_GDB", 14)) {
2358 #if defined(PANICTRACE)
2359 if (n
< 0 || n
> 2) {
2360 raw_printf("Illegal value in PANICTRACE_GDB (not 0,1,2).");
2364 sysopt
.panictrace_gdb
= n
;
2365 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "GDBPATH", 7)) {
2366 #if defined(PANICTRACE) && !defined(VMS)
2367 if (!file_exists(bufp
)) {
2368 raw_printf("File specified in GDBPATH does not exist.");
2373 free((genericptr_t
) sysopt
.gdbpath
);
2374 sysopt
.gdbpath
= dupstr(bufp
);
2375 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "GREPPATH", 7)) {
2376 #if defined(PANICTRACE) && !defined(VMS)
2377 if (!file_exists(bufp
)) {
2378 raw_printf("File specified in GREPPATH does not exist.");
2382 if (sysopt
.greppath
)
2383 free((genericptr_t
) sysopt
.greppath
);
2384 sysopt
.greppath
= dupstr(bufp
);
2387 } else if (match_varname(buf
, "BOULDER", 3)) {
2388 (void) get_uchars(fp
, buf
, bufp
, &iflags
.bouldersym
, TRUE
, 1,
2390 } else if (match_varname(buf
, "MENUCOLOR", 9)) {
2391 (void) add_menu_coloring(bufp
);
2392 } else if (match_varname(buf
, "WARNINGS", 5)) {
2393 (void) get_uchars(fp
, buf
, bufp
, translate
, FALSE
, WARNCOUNT
,
2395 assign_warnings(translate
);
2396 } else if (match_varname(buf
, "SYMBOLS", 4)) {
2397 char *op
, symbuf
[BUFSZ
];
2401 /* check for line continuation (trailing '\') */
2403 morelines
= (--op
>= bufp
&& *op
== '\\');
2406 /* strip trailing space now that '\' is gone */
2407 if (--op
>= bufp
&& *op
== ' ')
2411 if (!parsesymbols(bufp
)) {
2412 raw_printf("Error in SYMBOLS definition '%s'.\n", bufp
);
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
];
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 (!parse_config_line(fp
, strip_newline(buf
), src
)) {
2607 static const char badoptionline
[] = "Bad option line: \"%s\"";
2609 /* truncate buffer if it's long; this is actually conservative */
2610 if (strlen(buf
) > BUFSZ
- sizeof badoptionline
)
2611 buf
[BUFSZ
- sizeof badoptionline
] = '\0';
2613 raw_printf(badoptionline
, buf
);
2620 /* turn off detection of duplicate configfile options */
2621 set_duplicate_opt_detection(0);
2629 #if defined(VMS) || defined(UNIX)
2630 char tmp_wizkit
[BUFSZ
];
2634 envp
= nh_getenv("WIZKIT");
2636 (void) strncpy(wizkit
, envp
, WIZKIT_MAX
- 1);
2641 if (access(wizkit
, 4) == -1) {
2642 /* 4 is R_OK on newer systems */
2643 /* nasty sneaky attempt to read file through
2644 * NetHack's setuid permissions -- this is a
2645 * place a file name may be wholly under the player's
2648 raw_printf("Access to %s denied (%d).", wizkit
, errno
);
2650 /* fall through to standard names */
2653 if ((fp
= fopenp(wizkit
, "r")) != (FILE *) 0) {
2655 #if defined(UNIX) || defined(VMS)
2657 /* access() above probably caught most problems for UNIX */
2658 raw_printf("Couldn't open requested config file %s (%d).", wizkit
,
2664 #if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32)
2665 if ((fp
= fopenp(fqname(wizkit
, CONFIGPREFIX
, 0), "r")) != (FILE *) 0)
2669 envp
= nh_getenv("HOME");
2671 Sprintf(tmp_wizkit
, "%s%s", envp
, wizkit
);
2673 Sprintf(tmp_wizkit
, "%s%s", "sys$login:", wizkit
);
2674 if ((fp
= fopenp(tmp_wizkit
, "r")) != (FILE *) 0)
2676 #else /* should be only UNIX left */
2677 envp
= nh_getenv("HOME");
2679 Sprintf(tmp_wizkit
, "%s/%s", envp
, wizkit
);
2681 Strcpy(tmp_wizkit
, wizkit
);
2682 if ((fp
= fopenp(tmp_wizkit
, "r")) != (FILE *) 0)
2684 else if (errno
!= ENOENT
) {
2685 /* e.g., problems when setuid NetHack can't search home
2686 * directory restricted to user */
2687 raw_printf("Couldn't open default wizkit file %s (%d).", tmp_wizkit
,
2696 /* add to hero's inventory if there's room, otherwise put item on floor */
2701 if (!obj
|| obj
== &zeroobj
)
2704 /* subset of starting inventory pre-ID */
2706 if (Role_if(PM_PRIEST
))
2708 /* same criteria as lift_object()'s check for available inventory slot */
2709 if (obj
->oclass
!= COIN_CLASS
&& inv_cnt(FALSE
) >= 52
2710 && !merge_choice(invent
, obj
)) {
2711 /* inventory overflow; can't just place & stack object since
2712 hero isn't in position yet, so schedule for arrival later */
2713 add_to_migration(obj
);
2714 obj
->ox
= 0; /* index of main dungeon */
2715 obj
->oy
= 1; /* starting level number */
2717 (long) (MIGR_WITH_HERO
| MIGR_NOBREAK
| MIGR_NOSCATTER
);
2727 char *ep
, buf
[BUFSZ
];
2729 boolean bad_items
= FALSE
, skip
= FALSE
;
2731 if (!wizard
|| !(fp
= fopen_wizkit_file()))
2734 program_state
.wizkit_wishing
= 1;
2735 while (fgets(buf
, (int) (sizeof buf
), fp
)) {
2736 ep
= index(buf
, '\n');
2737 if (skip
) { /* in case previous line was too long */
2739 skip
= FALSE
; /* found newline; next line is normal */
2742 skip
= TRUE
; /* newline missing; discard next fgets */
2744 *ep
= '\0'; /* remove newline */
2747 otmp
= readobjnam(buf
, (struct obj
*) 0);
2749 if (otmp
!= &zeroobj
)
2750 wizkit_addinv(otmp
);
2752 /* .60 limits output line width to 79 chars */
2753 raw_printf("Bad wizkit item: \"%.60s\"", buf
);
2759 program_state
.wizkit_wishing
= 0;
2766 extern struct symsetentry
*symset_list
; /* options.c */
2767 extern struct symparse loadsyms
[]; /* drawing.c */
2768 extern const char *known_handling
[]; /* drawing.c */
2769 extern const char *known_restrictions
[]; /* drawing.c */
2770 static int symset_count
= 0; /* for pick-list building only */
2771 static boolean chosen_symset_start
= FALSE
, chosen_symset_end
= FALSE
;
2779 fp
= fopen_datafile(SYMBOLS
, "r", HACKPREFIX
);
2784 * Returns 1 if the chose symset was found and loaded.
2785 * 0 if it wasn't found in the sym file or other problem.
2788 read_sym_file(which_set
)
2791 char buf
[4 * BUFSZ
];
2794 if (!(fp
= fopen_sym_file()))
2798 chosen_symset_start
= chosen_symset_end
= FALSE
;
2799 while (fgets(buf
, 4 * BUFSZ
, fp
)) {
2800 if (!parse_sym_line(buf
, which_set
)) {
2801 raw_printf("Bad symbol line: \"%.50s\"", buf
);
2806 if (!chosen_symset_start
&& !chosen_symset_end
) {
2807 /* name caller put in symset[which_set].name was not found;
2808 if it looks like "Default symbols", null it out and return
2809 success to use the default; otherwise, return failure */
2810 if (symset
[which_set
].name
2811 && (fuzzymatch(symset
[which_set
].name
, "Default symbols",
2813 || !strcmpi(symset
[which_set
].name
, "default")))
2814 clear_symsetentry(which_set
, TRUE
);
2815 return (symset
[which_set
].name
== 0) ? 1 : 0;
2817 if (!chosen_symset_end
) {
2818 raw_printf("Missing finish for symset \"%s\"",
2819 symset
[which_set
].name
? symset
[which_set
].name
2826 /* returns 0 on error */
2828 parse_sym_line(buf
, which_set
)
2833 struct symparse
*symp
= (struct symparse
*) 0;
2834 char *bufp
, *commentp
, *altp
;
2836 /* convert each instance of whitespace (tabs, consecutive spaces)
2837 into a single space; leading and trailing spaces are stripped */
2839 if (!*buf
|| *buf
== '#' || !strcmp(buf
, " "))
2841 /* remove trailing comment, if any (this isn't strictly needed for
2842 individual symbols, and it won't matter if "X#comment" without
2843 separating space slips through; for handling or set description,
2844 symbol set creator is responsible for preceding '#' with a space
2845 and that comment itself doesn't contain " #") */
2846 if ((commentp
= rindex(buf
, '#')) != 0 && commentp
[-1] == ' ')
2847 commentp
[-1] = '\0';
2849 /* find the '=' or ':' */
2850 bufp
= index(buf
, '=');
2851 altp
= index(buf
, ':');
2852 if (!bufp
|| (altp
&& altp
< bufp
))
2855 if (strncmpi(buf
, "finish", 6) == 0) {
2856 /* end current graphics set */
2857 if (chosen_symset_start
)
2858 chosen_symset_end
= TRUE
;
2859 chosen_symset_start
= FALSE
;
2864 /* skip '=' and space which follows, if any */
2869 symp
= match_sym(buf
);
2873 if (!symset
[which_set
].name
) {
2874 /* A null symset name indicates that we're just
2875 building a pick-list of possible symset
2876 values from the file, so only do that */
2877 if (symp
->range
== SYM_CONTROL
) {
2878 struct symsetentry
*tmpsp
;
2880 switch (symp
->idx
) {
2883 (struct symsetentry
*) alloc(sizeof (struct symsetentry
));
2884 tmpsp
->next
= (struct symsetentry
*) 0;
2886 symset_list
= tmpsp
;
2890 tmpsp
->next
= symset_list
;
2891 symset_list
= tmpsp
;
2893 tmpsp
->idx
= symset_count
;
2894 tmpsp
->name
= dupstr(bufp
);
2895 tmpsp
->desc
= (char *) 0;
2897 /* initialize restriction bits */
2902 /* handler type identified */
2903 tmpsp
= symset_list
; /* most recent symset */
2904 tmpsp
->handling
= H_UNK
;
2906 while (known_handling
[i
]) {
2907 if (!strcmpi(known_handling
[i
], bufp
)) {
2908 tmpsp
->handling
= i
;
2909 break; /* while loop */
2914 case 3: /* description:something */
2915 tmpsp
= symset_list
; /* most recent symset */
2916 if (tmpsp
&& !tmpsp
->desc
)
2917 tmpsp
->desc
= dupstr(bufp
);
2920 /* restrictions: xxxx*/
2921 tmpsp
= symset_list
; /* most recent symset */
2922 for (i
= 0; known_restrictions
[i
]; ++i
) {
2923 if (!strcmpi(known_restrictions
[i
], bufp
)) {
2932 break; /* while loop */
2941 if (symp
->range
== SYM_CONTROL
) {
2942 switch (symp
->idx
) {
2944 /* start of symset */
2945 if (!strcmpi(bufp
, symset
[which_set
].name
)) {
2946 /* matches desired one */
2947 chosen_symset_start
= TRUE
;
2948 /* these init_*() functions clear symset fields too */
2949 if (which_set
== ROGUESET
)
2951 else if (which_set
== PRIMARY
)
2957 if (chosen_symset_start
)
2958 chosen_symset_end
= TRUE
;
2959 chosen_symset_start
= FALSE
;
2962 /* handler type identified */
2963 if (chosen_symset_start
)
2964 set_symhandling(bufp
, which_set
);
2966 /* case 3: (description) is ignored here */
2967 case 4: /* color:off */
2968 if (chosen_symset_start
) {
2970 if (!strcmpi(bufp
, "true") || !strcmpi(bufp
, "yes")
2971 || !strcmpi(bufp
, "on"))
2972 symset
[which_set
].nocolor
= 0;
2973 else if (!strcmpi(bufp
, "false")
2974 || !strcmpi(bufp
, "no")
2975 || !strcmpi(bufp
, "off"))
2976 symset
[which_set
].nocolor
= 1;
2980 case 5: /* restrictions: xxxx*/
2981 if (chosen_symset_start
) {
2984 while (known_restrictions
[n
]) {
2985 if (!strcmpi(known_restrictions
[n
], bufp
)) {
2988 symset
[which_set
].primary
= 1;
2991 symset
[which_set
].rogue
= 1;
2994 break; /* while loop */
3001 } else { /* !SYM_CONTROL */
3002 val
= sym_val(bufp
);
3003 if (chosen_symset_start
) {
3004 if (which_set
== PRIMARY
) {
3005 update_l_symset(symp
, val
);
3006 } else if (which_set
== ROGUESET
) {
3007 update_r_symset(symp
, val
);
3016 set_symhandling(handling
, which_set
)
3022 symset
[which_set
].handling
= H_UNK
;
3023 while (known_handling
[i
]) {
3024 if (!strcmpi(known_handling
[i
], handling
)) {
3025 symset
[which_set
].handling
= i
;
3032 /* ---------- END CONFIG FILE HANDLING ----------- */
3034 /* ---------- BEGIN SCOREBOARD CREATION ----------- */
3037 #define UNUSED_if_not_OS2_CODEVIEW /*empty*/
3039 #define UNUSED_if_not_OS2_CODEVIEW UNUSED
3042 /* verify that we can write to scoreboard file; if not, try to create one */
3045 check_recordfile(dir
)
3046 const char *dir UNUSED_if_not_OS2_CODEVIEW
;
3048 #if defined(PRAGMA_UNUSED) && !defined(OS2_CODEVIEW)
3051 const char *fq_record
;
3054 #if defined(UNIX) || defined(VMS)
3055 fq_record
= fqname(RECORD
, SCOREPREFIX
, 0);
3056 fd
= open(fq_record
, O_RDWR
, 0);
3058 #ifdef VMS /* must be stream-lf to use UPDATE_RECORD_IN_PLACE */
3059 if (!file_is_stmlf(fd
)) {
3061 "Warning: scoreboard file %s is not in stream_lf format",
3066 (void) nhclose(fd
); /* RECORD is accessible */
3067 } else if ((fd
= open(fq_record
, O_CREAT
| O_RDWR
, FCMASK
)) >= 0) {
3068 (void) nhclose(fd
); /* RECORD newly created */
3069 #if defined(VMS) && !defined(SECURE)
3070 /* Re-protect RECORD with world:read+write+execute+delete access. */
3071 (void) chmod(fq_record
, FCMASK
| 007);
3072 #endif /* VMS && !SECURE */
3074 raw_printf("Warning: cannot write scoreboard file %s", fq_record
);
3077 #endif /* !UNIX && !VMS */
3078 #if defined(MICRO) || defined(WIN32)
3081 #ifdef OS2_CODEVIEW /* explicit path on opening for OS/2 */
3082 /* how does this work when there isn't an explicit path or fopenp
3083 * for later access to the file via fopen_datafile? ? */
3084 (void) strncpy(tmp
, dir
, PATHLEN
- 1);
3085 tmp
[PATHLEN
- 1] = '\0';
3086 if ((strlen(tmp
) + 1 + strlen(RECORD
)) < (PATHLEN
- 1)) {
3088 Strcat(tmp
, RECORD
);
3092 Strcpy(tmp
, RECORD
);
3093 fq_record
= fqname(RECORD
, SCOREPREFIX
, 0);
3096 if ((fd
= open(fq_record
, O_RDWR
)) < 0) {
3097 /* try to create empty record */
3098 #if defined(AZTEC_C) || defined(_DCC) \
3099 || (defined(__GNUC__) && defined(__AMIGA__))
3100 /* Aztec doesn't use the third argument */
3101 /* DICE doesn't like it */
3102 if ((fd
= open(fq_record
, O_CREAT
| O_RDWR
)) < 0) {
3104 if ((fd
= open(fq_record
, O_CREAT
| O_RDWR
, S_IREAD
| S_IWRITE
))
3107 raw_printf("Warning: cannot write record %s", tmp
);
3111 } else /* open succeeded */
3113 #else /* MICRO || WIN32*/
3116 /* Create the "record" file, if necessary */
3117 fq_record
= fqname(RECORD
, SCOREPREFIX
, 0);
3118 fd
= macopen(fq_record
, O_RDWR
| O_CREAT
, TEXT_TYPE
);
3123 #endif /* MICRO || WIN32*/
3126 /* ---------- END SCOREBOARD CREATION ----------- */
3128 /* ---------- BEGIN PANIC/IMPOSSIBLE LOG ----------- */
3132 paniclog(type
, reason
)
3133 const char *type
; /* panic, impossible, trickery */
3134 const char *reason
; /* explanation */
3140 if (!program_state
.in_paniclog
) {
3141 program_state
.in_paniclog
= 1;
3142 lfile
= fopen_datafile(PANICLOG
, "a", TROUBLEPREFIX
);
3144 #ifdef PANICLOG_FMT2
3145 (void) fprintf(lfile
, "%ld %s: %s %s\n",
3146 ubirthday
, (plname
? plname
: "(none)"),
3149 time_t now
= getnow();
3151 char playmode
= wizard
? 'D' : discover
? 'X' : '-';
3153 (void) fprintf(lfile
, "%s %08ld %06ld %d %c: %s %s\n",
3154 version_string(buf
), yyyymmdd(now
), hhmmss(now
),
3155 uid
, playmode
, type
, reason
);
3156 #endif /* !PANICLOG_FMT2 */
3157 (void) fclose(lfile
);
3159 program_state
.in_paniclog
= 0;
3161 #endif /* PANICLOG */
3165 /* ---------- END PANIC/IMPOSSIBLE LOG ----------- */
3169 /* ---------- BEGIN INTERNAL RECOVER ----------- */
3174 int lev
, savelev
, hpid
, pltmpsiz
;
3176 struct version_info version_data
;
3178 char savename
[SAVESIZE
], errbuf
[BUFSZ
];
3179 struct savefile_info sfi
;
3180 char tmpplbuf
[PL_NSIZ
];
3182 for (lev
= 0; lev
< 256; lev
++)
3185 /* level 0 file contains:
3186 * pid of creating process (ignored here)
3187 * level number for current level of save file
3188 * name of save file nethack would have created
3193 gfd
= open_levelfile(0, errbuf
);
3195 raw_printf("%s\n", errbuf
);
3198 if (read(gfd
, (genericptr_t
) &hpid
, sizeof hpid
) != sizeof hpid
) {
3199 raw_printf("\n%s\n%s\n",
3200 "Checkpoint data incompletely written or subsequently clobbered.",
3201 "Recovery impossible.");
3202 (void) nhclose(gfd
);
3205 if (read(gfd
, (genericptr_t
) &savelev
, sizeof(savelev
))
3206 != sizeof(savelev
)) {
3208 "\nCheckpointing was not in effect for %s -- recovery impossible.\n",
3210 (void) nhclose(gfd
);
3213 if ((read(gfd
, (genericptr_t
) savename
, sizeof savename
)
3215 || (read(gfd
, (genericptr_t
) &version_data
, sizeof version_data
)
3216 != sizeof version_data
)
3217 || (read(gfd
, (genericptr_t
) &sfi
, sizeof sfi
) != sizeof sfi
)
3218 || (read(gfd
, (genericptr_t
) &pltmpsiz
, sizeof pltmpsiz
)
3219 != sizeof pltmpsiz
) || (pltmpsiz
> PL_NSIZ
)
3220 || (read(gfd
, (genericptr_t
) &tmpplbuf
, pltmpsiz
) != pltmpsiz
)) {
3221 raw_printf("\nError reading %s -- can't recover.\n", lock
);
3222 (void) nhclose(gfd
);
3226 /* save file should contain:
3230 * current level (including pets)
3231 * (non-level-based) game state
3234 set_savefile_name(TRUE
);
3235 sfd
= create_savefile();
3237 raw_printf("\nCannot recover savefile %s.\n", SAVEF
);
3238 (void) nhclose(gfd
);
3242 lfd
= open_levelfile(savelev
, errbuf
);
3244 raw_printf("\n%s\n", errbuf
);
3245 (void) nhclose(gfd
);
3246 (void) nhclose(sfd
);
3251 if (write(sfd
, (genericptr_t
) &version_data
, sizeof version_data
)
3252 != sizeof version_data
) {
3253 raw_printf("\nError writing %s; recovery failed.", SAVEF
);
3254 (void) nhclose(gfd
);
3255 (void) nhclose(sfd
);
3260 if (write(sfd
, (genericptr_t
) &sfi
, sizeof sfi
) != sizeof sfi
) {
3261 raw_printf("\nError writing %s; recovery failed (savefile_info).\n",
3263 (void) nhclose(gfd
);
3264 (void) nhclose(sfd
);
3269 if (write(sfd
, (genericptr_t
) &pltmpsiz
, sizeof pltmpsiz
)
3270 != sizeof pltmpsiz
) {
3271 raw_printf("Error writing %s; recovery failed (player name size).\n",
3273 (void) nhclose(gfd
);
3274 (void) nhclose(sfd
);
3279 if (write(sfd
, (genericptr_t
) &tmpplbuf
, pltmpsiz
) != pltmpsiz
) {
3280 raw_printf("Error writing %s; recovery failed (player name).\n",
3282 (void) nhclose(gfd
);
3283 (void) nhclose(sfd
);
3288 if (!copy_bytes(lfd
, sfd
)) {
3289 (void) nhclose(lfd
);
3290 (void) nhclose(sfd
);
3294 (void) nhclose(lfd
);
3295 processed
[savelev
] = 1;
3297 if (!copy_bytes(gfd
, sfd
)) {
3298 (void) nhclose(lfd
);
3299 (void) nhclose(sfd
);
3303 (void) nhclose(gfd
);
3306 for (lev
= 1; lev
< 256; lev
++) {
3307 /* level numbers are kept in xchars in save.c, so the
3308 * maximum level number (for the endlevel) must be < 256
3310 if (lev
!= savelev
) {
3311 lfd
= open_levelfile(lev
, (char *) 0);
3313 /* any or all of these may not exist */
3315 write(sfd
, (genericptr_t
) &levc
, sizeof(levc
));
3316 if (!copy_bytes(lfd
, sfd
)) {
3317 (void) nhclose(lfd
);
3318 (void) nhclose(sfd
);
3322 (void) nhclose(lfd
);
3327 (void) nhclose(sfd
);
3329 #ifdef HOLD_LOCKFILE_OPEN
3333 * We have a successful savefile!
3334 * Only now do we erase the level files.
3336 for (lev
= 0; lev
< 256; lev
++) {
3337 if (processed
[lev
]) {
3338 const char *fq_lock
;
3339 set_levelfile_name(lock
, lev
);
3340 fq_lock
= fqname(lock
, LEVELPREFIX
, 3);
3341 (void) unlink(fq_lock
);
3348 copy_bytes(ifd
, ofd
)
3355 nfrom
= read(ifd
, buf
, BUFSIZ
);
3356 nto
= write(ofd
, buf
, nfrom
);
3359 } while (nfrom
== BUFSIZ
);
3363 /* ---------- END INTERNAL RECOVER ----------- */
3364 #endif /*SELF_RECOVER*/
3366 /* ---------- OTHER ----------- */
3376 * All we really care about is the end result - can we read the file?
3377 * So just check that directly.
3379 * Not tested on most of the old platforms (which don't attempt
3380 * to implement SYSCF).
3381 * Some ports don't like open()'s optional third argument;
3382 * VMS overrides open() usage with a macro which requires it.
3385 # if defined(NOCWD_ASSUMPTIONS) && defined(WIN32)
3386 fd
= open(fqname(SYSCF_FILE
, SYSCONFPREFIX
, 0), O_RDONLY
);
3388 fd
= open(SYSCF_FILE
, O_RDONLY
);
3391 fd
= open(SYSCF_FILE
, O_RDONLY
, 0);
3398 raw_printf("Unable to open SYSCF_FILE.\n");
3402 #endif /* SYSCF_FILE */
3406 /* used by debugpline() to decide whether to issue a message
3407 * from a particular source file; caller passes __FILE__ and we check
3408 * whether it is in the source file list supplied by SYSCF's DEBUGFILES
3410 * pass FALSE to override wildcard matching; useful for files
3411 * like dungeon.c and questpgr.c, which generate a ridiculous amount of
3412 * output if DEBUG is defined and effectively block the use of a wildcard */
3414 debugcore(filename
, wildcards
)
3415 const char *filename
;
3418 const char *debugfiles
, *p
;
3420 if (!filename
|| !*filename
)
3421 return FALSE
; /* sanity precaution */
3423 if (sysopt
.env_dbgfl
== 0) {
3424 /* check once for DEBUGFILES in the environment;
3425 if found, it supersedes the sysconf value
3426 [note: getenv() rather than nh_getenv() since a long value
3427 is valid and doesn't pose any sort of overflow risk here] */
3428 if ((p
= getenv("DEBUGFILES")) != 0) {
3429 if (sysopt
.debugfiles
)
3430 free((genericptr_t
) sysopt
.debugfiles
);
3431 sysopt
.debugfiles
= dupstr(p
);
3432 sysopt
.env_dbgfl
= 1;
3434 sysopt
.env_dbgfl
= -1;
3437 debugfiles
= sysopt
.debugfiles
;
3438 /* usual case: sysopt.debugfiles will be empty */
3439 if (!debugfiles
|| !*debugfiles
)
3442 /* strip filename's path if present */
3444 if ((p
= rindex(filename
, '/')) != 0)
3448 filename
= vms_basename(filename
);
3449 /* vms_basename strips off 'type' suffix as well as path and version;
3450 we want to put suffix back (".c" assumed); since it always returns
3451 a pointer to a static buffer, we can safely modify its result */
3452 Strcat((char *) filename
, ".c");
3456 * Wildcard match will only work if there's a single pattern (which
3457 * might be a single file name without any wildcarding) rather than
3458 * a space-separated list.
3459 * [to NOT do: We could step through the space-separated list and
3460 * attempt a wildcard match against each element, but that would be
3461 * overkill for the intended usage.]
3463 if (wildcards
&& pmatch(debugfiles
, filename
))
3466 /* check whether filename is an element of the list */
3467 if ((p
= strstr(debugfiles
, filename
)) != 0) {
3468 int l
= (int) strlen(filename
);
3470 if ((p
== debugfiles
|| p
[-1] == ' ' || p
[-1] == '/')
3471 && (p
[l
] == ' ' || p
[l
] == '\0'))
3479 /* ---------- BEGIN TRIBUTE ----------- */
3484 #define SECTIONSCOPE 1
3485 #define TITLESCOPE 2
3486 #define PASSAGESCOPE 3
3488 #define MAXPASSAGES SIZE(context.novel.pasg) /* 20 */
3490 static int FDECL(choose_passage
, (int, unsigned));
3492 /* choose a random passage that hasn't been chosen yet; once all have
3493 been chosen, reset the tracking to make all passages available again */
3495 choose_passage(passagecnt
, oid
)
3496 int passagecnt
; /* total of available passages */
3497 unsigned oid
; /* book.o_id, used to determine whether re-reading same book */
3504 /* if a different book or we've used up all the passages already,
3505 reset in order to have all 'passagecnt' passages available */
3506 if (oid
!= context
.novel
.id
|| context
.novel
.count
== 0) {
3507 int i
, range
= passagecnt
, limit
= MAXPASSAGES
;
3509 context
.novel
.id
= oid
;
3510 if (range
<= limit
) {
3511 /* collect all of the N indices */
3512 context
.novel
.count
= passagecnt
;
3513 for (idx
= 0; idx
< MAXPASSAGES
; idx
++)
3514 context
.novel
.pasg
[idx
] = (xchar
) ((idx
< passagecnt
)
3517 /* collect MAXPASSAGES of the N indices */
3518 context
.novel
.count
= MAXPASSAGES
;
3519 for (idx
= i
= 0; i
< passagecnt
; ++i
, --range
)
3520 if (range
> 0 && rn2(range
) < limit
) {
3521 context
.novel
.pasg
[idx
++] = (xchar
) (i
+ 1);
3527 idx
= rn2(context
.novel
.count
);
3528 res
= (int) context
.novel
.pasg
[idx
];
3529 /* move the last slot's passage index into the slot just used
3530 and reduce the number of passages available */
3531 context
.novel
.pasg
[idx
] = context
.novel
.pasg
[--context
.novel
.count
];
3535 /* Returns True if you were able to read something. */
3537 read_tribute(tribsection
, tribtitle
, tribpassage
, nowin_buf
, bufsz
, oid
)
3538 const char *tribsection
, *tribtitle
;
3539 int tribpassage
, bufsz
;
3541 unsigned oid
; /* book identifier */
3544 char line
[BUFSZ
], lastline
[BUFSZ
];
3547 int linect
= 0, passagecnt
= 0, targetpassage
= 0;
3548 const char *badtranslation
= "an incomprehensible foreign translation";
3549 boolean matchedsection
= FALSE
, matchedtitle
= FALSE
;
3550 winid tribwin
= WIN_ERR
;
3551 boolean grasped
= FALSE
;
3552 boolean foundpassage
= FALSE
;
3557 /* check for mandatories */
3558 if (!tribsection
|| !tribtitle
) {
3560 pline("It's %s of \"%s\"!", badtranslation
, tribtitle
);
3564 debugpline3("read_tribute %s, %s, %d.", tribsection
, tribtitle
,
3567 fp
= dlb_fopen(TRIBUTEFILE
, "r");
3569 /* this is actually an error - cannot open tribute file! */
3571 pline("You feel too overwhelmed to continue!");
3576 * Syntax (not case-sensitive):
3579 * In the books section:
3580 * %title booktitle (n)
3581 * where booktitle=book title without quotes
3582 * (n)= total number of passages present for this title
3584 * where k=sequential passage number
3586 * %e ends the passage/book/section
3587 * If in a passage, it marks the end of that passage.
3588 * If in a book, it marks the end of that book.
3589 * If in a section, it marks the end of that section.
3594 *line
= *lastline
= '\0';
3595 while (dlb_fgets(line
, sizeof line
, fp
) != 0) {
3597 (void) strip_newline(line
);
3600 if (!strncmpi(&line
[1], "section ", sizeof "section " - 1)) {
3601 char *st
= &line
[9]; /* 9 from "%section " */
3603 scope
= SECTIONSCOPE
;
3604 matchedsection
= !strcmpi(st
, tribsection
) ? TRUE
: FALSE
;
3605 } else if (!strncmpi(&line
[1], "title ", sizeof "title " - 1)) {
3606 char *st
= &line
[7]; /* 7 from "%title " */
3609 if ((p1
= index(st
, '(')) != 0) {
3611 (void) mungspaces(st
);
3612 if ((p2
= index(p1
, ')')) != 0) {
3614 passagecnt
= atoi(p1
);
3616 if (matchedsection
&& !strcmpi(st
, tribtitle
)) {
3617 matchedtitle
= TRUE
;
3618 targetpassage
= !tribpassage
3619 ? choose_passage(passagecnt
, oid
)
3620 : (tribpassage
<= passagecnt
)
3623 matchedtitle
= FALSE
;
3627 } else if (!strncmpi(&line
[1], "passage ",
3628 sizeof "passage " - 1)) {
3630 char *st
= &line
[9]; /* 9 from "%passage " */
3633 passagenum
= atoi(st
);
3634 if (passagenum
> 0 && passagenum
<= passagecnt
) {
3635 scope
= PASSAGESCOPE
;
3636 if (matchedtitle
&& passagenum
== targetpassage
) {
3637 foundpassage
= TRUE
;
3639 tribwin
= create_nhwindow(NHW_MENU
);
3640 if (tribwin
== WIN_ERR
)
3645 } else if (!strncmpi(&line
[1], "e ", sizeof "e " - 1)) {
3648 if (scope
== TITLESCOPE
)
3649 matchedtitle
= FALSE
;
3650 if (scope
== SECTIONSCOPE
)
3651 matchedsection
= FALSE
;
3655 debugpline1("tribute file error: bad %% command, line %d.",
3660 /* comment only, next! */
3665 /* outputting multi-line passage to text window */
3666 putstr(tribwin
, 0, line
);
3668 Strcpy(lastline
, line
);
3670 /* fetching one-line passage into buffer */
3671 copynchars(nowin_buf
, line
, bufsz
- 1);
3672 goto cleanup
; /* don't wait for "%e passage" */
3679 (void) dlb_fclose(fp
);
3681 /* one-line buffer */
3682 grasped
= *nowin_buf
? TRUE
: FALSE
;
3684 if (tribwin
!= WIN_ERR
) { /* implies 'foundpassage' */
3685 /* multi-line window, normal case;
3686 if lastline is empty, there were no non-empty lines between
3687 "%passage n" and "%e passage" so we leave 'grasped' False */
3689 display_nhwindow(tribwin
, FALSE
);
3690 /* put the final attribution line into message history,
3691 analogous to the summary line from long quest messages */
3692 if (index(lastline
, '['))
3693 mungspaces(lastline
); /* to remove leading spaces */
3694 else /* construct one if necessary */
3695 Sprintf(lastline
, "[%s, by Terry Pratchett]", tribtitle
);
3696 putmsghistory(lastline
, FALSE
);
3699 destroy_nhwindow(tribwin
);
3702 /* multi-line window, problem */
3703 pline("It seems to be %s of \"%s\"!", badtranslation
, tribtitle
);
3709 Death_quote(buf
, bufsz
)
3713 unsigned death_oid
= 1; /* chance of oid #1 being a novel is negligible */
3715 return read_tribute("Death", "Death Quotes", 0, buf
, bufsz
, death_oid
);
3718 /* ---------- END TRIBUTE ----------- */