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
, "MSGTYPE", 7)) {
2156 (void) msgtype_parse_add(bufp
);
2157 #ifdef NOCWD_ASSUMPTIONS
2158 } else if (match_varname(buf
, "HACKDIR", 4)) {
2159 adjust_prefix(bufp
, HACKPREFIX
);
2160 } else if (match_varname(buf
, "LEVELDIR", 4)
2161 || match_varname(buf
, "LEVELS", 4)) {
2162 adjust_prefix(bufp
, LEVELPREFIX
);
2163 } else if (match_varname(buf
, "SAVEDIR", 4)) {
2164 adjust_prefix(bufp
, SAVEPREFIX
);
2165 } else if (match_varname(buf
, "BONESDIR", 5)) {
2166 adjust_prefix(bufp
, BONESPREFIX
);
2167 } else if (match_varname(buf
, "DATADIR", 4)) {
2168 adjust_prefix(bufp
, DATAPREFIX
);
2169 } else if (match_varname(buf
, "SCOREDIR", 4)) {
2170 adjust_prefix(bufp
, SCOREPREFIX
);
2171 } else if (match_varname(buf
, "LOCKDIR", 4)) {
2172 adjust_prefix(bufp
, LOCKPREFIX
);
2173 } else if (match_varname(buf
, "CONFIGDIR", 4)) {
2174 adjust_prefix(bufp
, CONFIGPREFIX
);
2175 } else if (match_varname(buf
, "TROUBLEDIR", 4)) {
2176 adjust_prefix(bufp
, TROUBLEPREFIX
);
2177 #else /*NOCWD_ASSUMPTIONS*/
2179 } else if (match_varname(buf
, "HACKDIR", 4)) {
2180 (void) strncpy(hackdir
, bufp
, PATHLEN
- 1);
2182 } else if (match_varname(buf
, "RAMDISK", 3)) {
2183 /* The following ifdef is NOT in the wrong
2184 * place. For now, we accept and silently
2187 if (strlen(bufp
) >= PATHLEN
)
2188 bufp
[PATHLEN
- 1] = '\0';
2189 Strcpy(levels
, bufp
);
2190 ramdisk
= (strcmp(permbones
, levels
) != 0);
2191 ramdisk_specified
= TRUE
;
2194 } else if (match_varname(buf
, "LEVELS", 4)) {
2195 if (strlen(bufp
) >= PATHLEN
)
2196 bufp
[PATHLEN
- 1] = '\0';
2197 Strcpy(permbones
, bufp
);
2198 if (!ramdisk_specified
|| !*levels
)
2199 Strcpy(levels
, bufp
);
2200 ramdisk
= (strcmp(permbones
, levels
) != 0);
2201 } else if (match_varname(buf
, "SAVE", 4)) {
2203 extern int saveprompt
;
2207 if ((ptr
= index(bufp
, ';')) != 0) {
2210 if (*(ptr
+ 1) == 'n' || *(ptr
+ 1) == 'N') {
2215 #if defined(SYSFLAGS) && defined(MFLOPPY)
2217 saveprompt
= sysflags
.asksavedisk
;
2220 (void) strncpy(SAVEP
, bufp
, SAVESIZE
- 1);
2221 append_slash(SAVEP
);
2223 #endif /*NOCWD_ASSUMPTIONS*/
2225 } else if (match_varname(buf
, "NAME", 4)) {
2226 (void) strncpy(plname
, bufp
, PL_NSIZ
- 1);
2227 } else if (match_varname(buf
, "ROLE", 4)
2228 || match_varname(buf
, "CHARACTER", 4)) {
2229 if ((len
= str2role(bufp
)) >= 0)
2230 flags
.initrole
= len
;
2231 } else if (match_varname(buf
, "DOGNAME", 3)) {
2232 (void) strncpy(dogname
, bufp
, PL_PSIZ
- 1);
2233 } else if (match_varname(buf
, "CATNAME", 3)) {
2234 (void) strncpy(catname
, bufp
, PL_PSIZ
- 1);
2237 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "WIZARDS", 7)) {
2239 free((genericptr_t
) sysopt
.wizards
);
2240 sysopt
.wizards
= dupstr(bufp
);
2241 if (strlen(sysopt
.wizards
) && strcmp(sysopt
.wizards
, "*")) {
2242 /* pre-format WIZARDS list now; it's displayed during a panic
2243 and since that panic might be due to running out of memory,
2244 we don't want to risk attempting to allocate any memory then */
2245 if (sysopt
.fmtd_wizard_list
)
2246 free((genericptr_t
) sysopt
.fmtd_wizard_list
);
2247 sysopt
.fmtd_wizard_list
= build_english_list(sysopt
.wizards
);
2249 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "SHELLERS", 8)) {
2250 if (sysopt
.shellers
)
2251 free((genericptr_t
) sysopt
.shellers
);
2252 sysopt
.shellers
= dupstr(bufp
);
2253 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "EXPLORERS", 7)) {
2254 if (sysopt
.explorers
)
2255 free((genericptr_t
) sysopt
.explorers
);
2256 sysopt
.explorers
= dupstr(bufp
);
2257 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "DEBUGFILES", 5)) {
2258 /* if showdebug() has already been called (perhaps we've added
2259 some debugpline() calls to option processing) and has found
2260 a value for getenv("DEBUGFILES"), don't override that */
2261 if (sysopt
.env_dbgfl
<= 0) {
2262 if (sysopt
.debugfiles
)
2263 free((genericptr_t
) sysopt
.debugfiles
);
2264 sysopt
.debugfiles
= dupstr(bufp
);
2266 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "GENERICUSERS", 12)) {
2267 if (sysopt
.genericusers
) free(sysopt
.genericusers
);
2268 sysopt
.genericusers
= dupstr(bufp
);
2269 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "SUPPORT", 7)) {
2271 free((genericptr_t
) sysopt
.support
);
2272 sysopt
.support
= dupstr(bufp
);
2273 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "RECOVER", 7)) {
2275 free((genericptr_t
) sysopt
.recover
);
2276 sysopt
.recover
= dupstr(bufp
);
2277 } else if (src
== SET_IN_SYS
2278 && match_varname(buf
, "CHECK_SAVE_UID", 14)) {
2280 sysopt
.check_save_uid
= n
;
2281 } else if (src
== SET_IN_SYS
2282 && match_varname(buf
, "CHECK_PLNAME", 12)) {
2284 sysopt
.check_plname
= n
;
2285 } else if (match_varname(buf
, "SEDUCE", 6)) {
2286 n
= !!atoi(bufp
); /* XXX this could be tighter */
2287 /* allow anyone to turn it off, but only sysconf to turn it on*/
2288 if (src
!= SET_IN_SYS
&& n
!= 0) {
2289 raw_printf("Illegal value in SEDUCE");
2293 sysopt_seduce_set(sysopt
.seduce
);
2294 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "MAXPLAYERS", 10)) {
2296 /* XXX to get more than 25, need to rewrite all lock code */
2297 if (n
< 0 || n
> 25) {
2298 raw_printf("Illegal value in MAXPLAYERS (maximum is 25).");
2301 sysopt
.maxplayers
= n
;
2302 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "PERSMAX", 7)) {
2305 raw_printf("Illegal value in PERSMAX (minimum is 1).");
2309 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "PERS_IS_UID", 11)) {
2311 if (n
!= 0 && n
!= 1) {
2312 raw_printf("Illegal value in PERS_IS_UID (must be 0 or 1).");
2315 sysopt
.pers_is_uid
= n
;
2316 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "ENTRYMAX", 8)) {
2319 raw_printf("Illegal value in ENTRYMAX (minimum is 10).");
2322 sysopt
.entrymax
= n
;
2323 } else if ((src
== SET_IN_SYS
) && match_varname(buf
, "POINTSMIN", 9)) {
2326 raw_printf("Illegal value in POINTSMIN (minimum is 1).");
2329 sysopt
.pointsmin
= n
;
2330 } else if (src
== SET_IN_SYS
2331 && match_varname(buf
, "MAX_STATUENAME_RANK", 10)) {
2335 "Illegal value in MAX_STATUENAME_RANK (minimum is 1).");
2338 sysopt
.tt_oname_maxrank
= n
;
2340 /* SYSCF PANICTRACE options */
2341 } else if (src
== SET_IN_SYS
2342 && match_varname(buf
, "PANICTRACE_LIBC", 15)) {
2344 #if defined(PANICTRACE) && defined(PANICTRACE_LIBC)
2345 if (n
< 0 || n
> 2) {
2346 raw_printf("Illegal value in PANICTRACE_LIBC (not 0,1,2).");
2350 sysopt
.panictrace_libc
= n
;
2351 } else if (src
== SET_IN_SYS
2352 && match_varname(buf
, "PANICTRACE_GDB", 14)) {
2354 #if defined(PANICTRACE)
2355 if (n
< 0 || n
> 2) {
2356 raw_printf("Illegal value in PANICTRACE_GDB (not 0,1,2).");
2360 sysopt
.panictrace_gdb
= n
;
2361 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "GDBPATH", 7)) {
2362 #if defined(PANICTRACE) && !defined(VMS)
2363 if (!file_exists(bufp
)) {
2364 raw_printf("File specified in GDBPATH does not exist.");
2369 free((genericptr_t
) sysopt
.gdbpath
);
2370 sysopt
.gdbpath
= dupstr(bufp
);
2371 } else if (src
== SET_IN_SYS
&& match_varname(buf
, "GREPPATH", 7)) {
2372 #if defined(PANICTRACE) && !defined(VMS)
2373 if (!file_exists(bufp
)) {
2374 raw_printf("File specified in GREPPATH does not exist.");
2378 if (sysopt
.greppath
)
2379 free((genericptr_t
) sysopt
.greppath
);
2380 sysopt
.greppath
= dupstr(bufp
);
2383 } else if (match_varname(buf
, "BOULDER", 3)) {
2384 (void) get_uchars(fp
, buf
, bufp
, &iflags
.bouldersym
, TRUE
, 1,
2386 } else if (match_varname(buf
, "MENUCOLOR", 9)) {
2387 (void) add_menu_coloring(bufp
);
2388 } else if (match_varname(buf
, "WARNINGS", 5)) {
2389 (void) get_uchars(fp
, buf
, bufp
, translate
, FALSE
, WARNCOUNT
,
2391 assign_warnings(translate
);
2392 } else if (match_varname(buf
, "SYMBOLS", 4)) {
2393 char *op
, symbuf
[BUFSZ
];
2397 /* check for line continuation (trailing '\') */
2399 morelines
= (--op
>= bufp
&& *op
== '\\');
2402 /* strip trailing space now that '\' is gone */
2403 if (--op
>= bufp
&& *op
== ' ')
2407 if (!parsesymbols(bufp
)) {
2408 raw_printf("Error in SYMBOLS definition '%s'.\n", bufp
);
2414 if (!fgets(symbuf
, BUFSZ
, fp
)) {
2420 } while (*bufp
== '#');
2422 } while (morelines
);
2423 switch_symbols(TRUE
);
2424 } else if (match_varname(buf
, "WIZKIT", 6)) {
2425 (void) strncpy(wizkit
, bufp
, WIZKIT_MAX
- 1);
2427 } else if (match_varname(buf
, "FONT", 4)) {
2430 if (t
= strchr(buf
+ 5, ':')) {
2432 amii_set_text_font(buf
+ 5, atoi(t
+ 1));
2435 } else if (match_varname(buf
, "PATH", 4)) {
2436 (void) strncpy(PATH
, bufp
, PATHLEN
- 1);
2437 } else if (match_varname(buf
, "DEPTH", 5)) {
2438 extern int amii_numcolors
;
2439 int val
= atoi(bufp
);
2441 amii_numcolors
= 1L << min(DEPTH
, val
);
2443 } else if (match_varname(buf
, "DRIPENS", 7)) {
2447 for (i
= 0, t
= strtok(bufp
, ",/"); t
!= (char *) 0;
2448 i
< 20 && (t
= strtok((char *) 0, ",/")), ++i
) {
2449 sscanf(t
, "%d", &val
);
2450 sysflags
.amii_dripens
[i
] = val
;
2453 } else if (match_varname(buf
, "SCREENMODE", 10)) {
2454 extern long amii_scrnmode
;
2456 if (!stricmp(bufp
, "req"))
2457 amii_scrnmode
= 0xffffffff; /* Requester */
2458 else if (sscanf(bufp
, "%x", &amii_scrnmode
) != 1)
2460 } else if (match_varname(buf
, "MSGPENS", 7)) {
2461 extern int amii_msgAPen
, amii_msgBPen
;
2462 char *t
= strtok(bufp
, ",/");
2465 sscanf(t
, "%d", &amii_msgAPen
);
2466 if (t
= strtok((char *) 0, ",/"))
2467 sscanf(t
, "%d", &amii_msgBPen
);
2469 } else if (match_varname(buf
, "TEXTPENS", 8)) {
2470 extern int amii_textAPen
, amii_textBPen
;
2471 char *t
= strtok(bufp
, ",/");
2474 sscanf(t
, "%d", &amii_textAPen
);
2475 if (t
= strtok((char *) 0, ",/"))
2476 sscanf(t
, "%d", &amii_textBPen
);
2478 } else if (match_varname(buf
, "MENUPENS", 8)) {
2479 extern int amii_menuAPen
, amii_menuBPen
;
2480 char *t
= strtok(bufp
, ",/");
2483 sscanf(t
, "%d", &amii_menuAPen
);
2484 if (t
= strtok((char *) 0, ",/"))
2485 sscanf(t
, "%d", &amii_menuBPen
);
2487 } else if (match_varname(buf
, "STATUSPENS", 10)) {
2488 extern int amii_statAPen
, amii_statBPen
;
2489 char *t
= strtok(bufp
, ",/");
2492 sscanf(t
, "%d", &amii_statAPen
);
2493 if (t
= strtok((char *) 0, ",/"))
2494 sscanf(t
, "%d", &amii_statBPen
);
2496 } else if (match_varname(buf
, "OTHERPENS", 9)) {
2497 extern int amii_otherAPen
, amii_otherBPen
;
2498 char *t
= strtok(bufp
, ",/");
2501 sscanf(t
, "%d", &amii_otherAPen
);
2502 if (t
= strtok((char *) 0, ",/"))
2503 sscanf(t
, "%d", &amii_otherBPen
);
2505 } else if (match_varname(buf
, "PENS", 4)) {
2506 extern unsigned short amii_init_map
[AMII_MAXCOLORS
];
2510 for (i
= 0, t
= strtok(bufp
, ",/");
2511 i
< AMII_MAXCOLORS
&& t
!= (char *) 0;
2512 t
= strtok((char *) 0, ",/"), ++i
) {
2513 sscanf(t
, "%hx", &amii_init_map
[i
]);
2515 amii_setpens(amii_numcolors
= i
);
2516 } else if (match_varname(buf
, "FGPENS", 6)) {
2517 extern int foreg
[AMII_MAXCOLORS
];
2521 for (i
= 0, t
= strtok(bufp
, ",/");
2522 i
< AMII_MAXCOLORS
&& t
!= (char *) 0;
2523 t
= strtok((char *) 0, ",/"), ++i
) {
2524 sscanf(t
, "%d", &foreg
[i
]);
2526 } else if (match_varname(buf
, "BGPENS", 6)) {
2527 extern int backg
[AMII_MAXCOLORS
];
2531 for (i
= 0, t
= strtok(bufp
, ",/");
2532 i
< AMII_MAXCOLORS
&& t
!= (char *) 0;
2533 t
= strtok((char *) 0, ",/"), ++i
) {
2534 sscanf(t
, "%d", &backg
[i
]);
2538 } else if (match_varname(buf
, "SOUNDDIR", 8)) {
2539 sounddir
= dupstr(bufp
);
2540 } else if (match_varname(buf
, "SOUND", 5)) {
2541 add_sound_mapping(bufp
);
2544 /* These should move to wc_ options */
2545 } else if (match_varname(buf
, "QT_TILEWIDTH", 12)) {
2546 extern char *qt_tilewidth
;
2548 if (qt_tilewidth
== NULL
)
2549 qt_tilewidth
= dupstr(bufp
);
2550 } else if (match_varname(buf
, "QT_TILEHEIGHT", 13)) {
2551 extern char *qt_tileheight
;
2553 if (qt_tileheight
== NULL
)
2554 qt_tileheight
= dupstr(bufp
);
2555 } else if (match_varname(buf
, "QT_FONTSIZE", 11)) {
2556 extern char *qt_fontsize
;
2558 if (qt_fontsize
== NULL
)
2559 qt_fontsize
= dupstr(bufp
);
2560 } else if (match_varname(buf
, "QT_COMPACT", 10)) {
2561 extern int qt_compact_mode
;
2563 qt_compact_mode
= atoi(bufp
);
2572 can_read_file(filename
)
2573 const char *filename
;
2575 return (boolean
) (access(filename
, 4) == 0);
2577 #endif /* USER_SOUNDS */
2580 read_config_file(filename
, src
)
2581 const char *filename
;
2584 char buf
[4 * BUFSZ
];
2586 boolean rv
= TRUE
; /* assume successful parse */
2588 if (!(fp
= fopen_config_file(filename
, src
)))
2591 /* begin detection of duplicate configfile options */
2592 set_duplicate_opt_detection(1);
2594 while (fgets(buf
, sizeof buf
, fp
)) {
2597 XXX Don't call read() in parse_config_line, read as callback or reassemble
2599 OR: Forbid multiline stuff for alternate config sources.
2602 if (!parse_config_line(fp
, strip_newline(buf
), src
)) {
2603 static const char badoptionline
[] = "Bad option line: \"%s\"";
2605 /* truncate buffer if it's long; this is actually conservative */
2606 if (strlen(buf
) > BUFSZ
- sizeof badoptionline
)
2607 buf
[BUFSZ
- sizeof badoptionline
] = '\0';
2609 raw_printf(badoptionline
, buf
);
2616 /* turn off detection of duplicate configfile options */
2617 set_duplicate_opt_detection(0);
2625 #if defined(VMS) || defined(UNIX)
2626 char tmp_wizkit
[BUFSZ
];
2630 envp
= nh_getenv("WIZKIT");
2632 (void) strncpy(wizkit
, envp
, WIZKIT_MAX
- 1);
2637 if (access(wizkit
, 4) == -1) {
2638 /* 4 is R_OK on newer systems */
2639 /* nasty sneaky attempt to read file through
2640 * NetHack's setuid permissions -- this is a
2641 * place a file name may be wholly under the player's
2644 raw_printf("Access to %s denied (%d).", wizkit
, errno
);
2646 /* fall through to standard names */
2649 if ((fp
= fopenp(wizkit
, "r")) != (FILE *) 0) {
2651 #if defined(UNIX) || defined(VMS)
2653 /* access() above probably caught most problems for UNIX */
2654 raw_printf("Couldn't open requested config file %s (%d).", wizkit
,
2660 #if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32)
2661 if ((fp
= fopenp(fqname(wizkit
, CONFIGPREFIX
, 0), "r")) != (FILE *) 0)
2665 envp
= nh_getenv("HOME");
2667 Sprintf(tmp_wizkit
, "%s%s", envp
, wizkit
);
2669 Sprintf(tmp_wizkit
, "%s%s", "sys$login:", wizkit
);
2670 if ((fp
= fopenp(tmp_wizkit
, "r")) != (FILE *) 0)
2672 #else /* should be only UNIX left */
2673 envp
= nh_getenv("HOME");
2675 Sprintf(tmp_wizkit
, "%s/%s", envp
, wizkit
);
2677 Strcpy(tmp_wizkit
, wizkit
);
2678 if ((fp
= fopenp(tmp_wizkit
, "r")) != (FILE *) 0)
2680 else if (errno
!= ENOENT
) {
2681 /* e.g., problems when setuid NetHack can't search home
2682 * directory restricted to user */
2683 raw_printf("Couldn't open default wizkit file %s (%d).", tmp_wizkit
,
2692 /* add to hero's inventory if there's room, otherwise put item on floor */
2697 if (!obj
|| obj
== &zeroobj
)
2700 /* subset of starting inventory pre-ID */
2702 if (Role_if(PM_PRIEST
))
2704 /* same criteria as lift_object()'s check for available inventory slot */
2705 if (obj
->oclass
!= COIN_CLASS
&& inv_cnt(FALSE
) >= 52
2706 && !merge_choice(invent
, obj
)) {
2707 /* inventory overflow; can't just place & stack object since
2708 hero isn't in position yet, so schedule for arrival later */
2709 add_to_migration(obj
);
2710 obj
->ox
= 0; /* index of main dungeon */
2711 obj
->oy
= 1; /* starting level number */
2713 (long) (MIGR_WITH_HERO
| MIGR_NOBREAK
| MIGR_NOSCATTER
);
2723 char *ep
, buf
[BUFSZ
];
2725 boolean bad_items
= FALSE
, skip
= FALSE
;
2727 if (!wizard
|| !(fp
= fopen_wizkit_file()))
2730 program_state
.wizkit_wishing
= 1;
2731 while (fgets(buf
, (int) (sizeof buf
), fp
)) {
2732 ep
= index(buf
, '\n');
2733 if (skip
) { /* in case previous line was too long */
2735 skip
= FALSE
; /* found newline; next line is normal */
2738 skip
= TRUE
; /* newline missing; discard next fgets */
2740 *ep
= '\0'; /* remove newline */
2743 otmp
= readobjnam(buf
, (struct obj
*) 0);
2745 if (otmp
!= &zeroobj
)
2746 wizkit_addinv(otmp
);
2748 /* .60 limits output line width to 79 chars */
2749 raw_printf("Bad wizkit item: \"%.60s\"", buf
);
2755 program_state
.wizkit_wishing
= 0;
2762 extern struct symsetentry
*symset_list
; /* options.c */
2763 extern struct symparse loadsyms
[]; /* drawing.c */
2764 extern const char *known_handling
[]; /* drawing.c */
2765 extern const char *known_restrictions
[]; /* drawing.c */
2766 static int symset_count
= 0; /* for pick-list building only */
2767 static boolean chosen_symset_start
= FALSE
, chosen_symset_end
= FALSE
;
2775 fp
= fopen_datafile(SYMBOLS
, "r", HACKPREFIX
);
2780 * Returns 1 if the chose symset was found and loaded.
2781 * 0 if it wasn't found in the sym file or other problem.
2784 read_sym_file(which_set
)
2787 char buf
[4 * BUFSZ
];
2790 if (!(fp
= fopen_sym_file()))
2794 chosen_symset_start
= chosen_symset_end
= FALSE
;
2795 while (fgets(buf
, 4 * BUFSZ
, fp
)) {
2796 if (!parse_sym_line(buf
, which_set
)) {
2797 raw_printf("Bad symbol line: \"%.50s\"", buf
);
2802 if (!chosen_symset_start
&& !chosen_symset_end
) {
2803 /* name caller put in symset[which_set].name was not found;
2804 if it looks like "Default symbols", null it out and return
2805 success to use the default; otherwise, return failure */
2806 if (symset
[which_set
].name
2807 && (fuzzymatch(symset
[which_set
].name
, "Default symbols",
2809 || !strcmpi(symset
[which_set
].name
, "default")))
2810 clear_symsetentry(which_set
, TRUE
);
2811 return (symset
[which_set
].name
== 0) ? 1 : 0;
2813 if (!chosen_symset_end
) {
2814 raw_printf("Missing finish for symset \"%s\"",
2815 symset
[which_set
].name
? symset
[which_set
].name
2822 /* returns 0 on error */
2824 parse_sym_line(buf
, which_set
)
2829 struct symparse
*symp
= (struct symparse
*) 0;
2830 char *bufp
, *commentp
, *altp
;
2832 /* convert each instance of whitespace (tabs, consecutive spaces)
2833 into a single space; leading and trailing spaces are stripped */
2835 if (!*buf
|| *buf
== '#' || !strcmp(buf
, " "))
2837 /* remove trailing comment, if any (this isn't strictly needed for
2838 individual symbols, and it won't matter if "X#comment" without
2839 separating space slips through; for handling or set description,
2840 symbol set creator is responsible for preceding '#' with a space
2841 and that comment itself doesn't contain " #") */
2842 if ((commentp
= rindex(buf
, '#')) != 0 && commentp
[-1] == ' ')
2843 commentp
[-1] = '\0';
2845 /* find the '=' or ':' */
2846 bufp
= index(buf
, '=');
2847 altp
= index(buf
, ':');
2848 if (!bufp
|| (altp
&& altp
< bufp
))
2851 if (strncmpi(buf
, "finish", 6) == 0) {
2852 /* end current graphics set */
2853 if (chosen_symset_start
)
2854 chosen_symset_end
= TRUE
;
2855 chosen_symset_start
= FALSE
;
2860 /* skip '=' and space which follows, if any */
2865 symp
= match_sym(buf
);
2869 if (!symset
[which_set
].name
) {
2870 /* A null symset name indicates that we're just
2871 building a pick-list of possible symset
2872 values from the file, so only do that */
2873 if (symp
->range
== SYM_CONTROL
) {
2874 struct symsetentry
*tmpsp
;
2876 switch (symp
->idx
) {
2879 (struct symsetentry
*) alloc(sizeof (struct symsetentry
));
2880 tmpsp
->next
= (struct symsetentry
*) 0;
2882 symset_list
= tmpsp
;
2886 tmpsp
->next
= symset_list
;
2887 symset_list
= tmpsp
;
2889 tmpsp
->idx
= symset_count
;
2890 tmpsp
->name
= dupstr(bufp
);
2891 tmpsp
->desc
= (char *) 0;
2893 /* initialize restriction bits */
2898 /* handler type identified */
2899 tmpsp
= symset_list
; /* most recent symset */
2900 tmpsp
->handling
= H_UNK
;
2902 while (known_handling
[i
]) {
2903 if (!strcmpi(known_handling
[i
], bufp
)) {
2904 tmpsp
->handling
= i
;
2905 break; /* while loop */
2910 case 3: /* description:something */
2911 tmpsp
= symset_list
; /* most recent symset */
2912 if (tmpsp
&& !tmpsp
->desc
)
2913 tmpsp
->desc
= dupstr(bufp
);
2916 /* restrictions: xxxx*/
2917 tmpsp
= symset_list
; /* most recent symset */
2918 for (i
= 0; known_restrictions
[i
]; ++i
) {
2919 if (!strcmpi(known_restrictions
[i
], bufp
)) {
2928 break; /* while loop */
2937 if (symp
->range
== SYM_CONTROL
) {
2938 switch (symp
->idx
) {
2940 /* start of symset */
2941 if (!strcmpi(bufp
, symset
[which_set
].name
)) {
2942 /* matches desired one */
2943 chosen_symset_start
= TRUE
;
2944 /* these init_*() functions clear symset fields too */
2945 if (which_set
== ROGUESET
)
2947 else if (which_set
== PRIMARY
)
2953 if (chosen_symset_start
)
2954 chosen_symset_end
= TRUE
;
2955 chosen_symset_start
= FALSE
;
2958 /* handler type identified */
2959 if (chosen_symset_start
)
2960 set_symhandling(bufp
, which_set
);
2962 /* case 3: (description) is ignored here */
2963 case 4: /* color:off */
2964 if (chosen_symset_start
) {
2966 if (!strcmpi(bufp
, "true") || !strcmpi(bufp
, "yes")
2967 || !strcmpi(bufp
, "on"))
2968 symset
[which_set
].nocolor
= 0;
2969 else if (!strcmpi(bufp
, "false")
2970 || !strcmpi(bufp
, "no")
2971 || !strcmpi(bufp
, "off"))
2972 symset
[which_set
].nocolor
= 1;
2976 case 5: /* restrictions: xxxx*/
2977 if (chosen_symset_start
) {
2980 while (known_restrictions
[n
]) {
2981 if (!strcmpi(known_restrictions
[n
], bufp
)) {
2984 symset
[which_set
].primary
= 1;
2987 symset
[which_set
].rogue
= 1;
2990 break; /* while loop */
2997 } else { /* !SYM_CONTROL */
2998 val
= sym_val(bufp
);
2999 if (chosen_symset_start
) {
3000 if (which_set
== PRIMARY
) {
3001 update_l_symset(symp
, val
);
3002 } else if (which_set
== ROGUESET
) {
3003 update_r_symset(symp
, val
);
3012 set_symhandling(handling
, which_set
)
3018 symset
[which_set
].handling
= H_UNK
;
3019 while (known_handling
[i
]) {
3020 if (!strcmpi(known_handling
[i
], handling
)) {
3021 symset
[which_set
].handling
= i
;
3028 /* ---------- END CONFIG FILE HANDLING ----------- */
3030 /* ---------- BEGIN SCOREBOARD CREATION ----------- */
3033 #define UNUSED_if_not_OS2_CODEVIEW /*empty*/
3035 #define UNUSED_if_not_OS2_CODEVIEW UNUSED
3038 /* verify that we can write to scoreboard file; if not, try to create one */
3041 check_recordfile(dir
)
3042 const char *dir UNUSED_if_not_OS2_CODEVIEW
;
3044 #if defined(PRAGMA_UNUSED) && !defined(OS2_CODEVIEW)
3047 const char *fq_record
;
3050 #if defined(UNIX) || defined(VMS)
3051 fq_record
= fqname(RECORD
, SCOREPREFIX
, 0);
3052 fd
= open(fq_record
, O_RDWR
, 0);
3054 #ifdef VMS /* must be stream-lf to use UPDATE_RECORD_IN_PLACE */
3055 if (!file_is_stmlf(fd
)) {
3057 "Warning: scoreboard file %s is not in stream_lf format",
3062 (void) nhclose(fd
); /* RECORD is accessible */
3063 } else if ((fd
= open(fq_record
, O_CREAT
| O_RDWR
, FCMASK
)) >= 0) {
3064 (void) nhclose(fd
); /* RECORD newly created */
3065 #if defined(VMS) && !defined(SECURE)
3066 /* Re-protect RECORD with world:read+write+execute+delete access. */
3067 (void) chmod(fq_record
, FCMASK
| 007);
3068 #endif /* VMS && !SECURE */
3070 raw_printf("Warning: cannot write scoreboard file %s", fq_record
);
3073 #endif /* !UNIX && !VMS */
3074 #if defined(MICRO) || defined(WIN32)
3077 #ifdef OS2_CODEVIEW /* explicit path on opening for OS/2 */
3078 /* how does this work when there isn't an explicit path or fopenp
3079 * for later access to the file via fopen_datafile? ? */
3080 (void) strncpy(tmp
, dir
, PATHLEN
- 1);
3081 tmp
[PATHLEN
- 1] = '\0';
3082 if ((strlen(tmp
) + 1 + strlen(RECORD
)) < (PATHLEN
- 1)) {
3084 Strcat(tmp
, RECORD
);
3088 Strcpy(tmp
, RECORD
);
3089 fq_record
= fqname(RECORD
, SCOREPREFIX
, 0);
3092 if ((fd
= open(fq_record
, O_RDWR
)) < 0) {
3093 /* try to create empty record */
3094 #if defined(AZTEC_C) || defined(_DCC) \
3095 || (defined(__GNUC__) && defined(__AMIGA__))
3096 /* Aztec doesn't use the third argument */
3097 /* DICE doesn't like it */
3098 if ((fd
= open(fq_record
, O_CREAT
| O_RDWR
)) < 0) {
3100 if ((fd
= open(fq_record
, O_CREAT
| O_RDWR
, S_IREAD
| S_IWRITE
))
3103 raw_printf("Warning: cannot write record %s", tmp
);
3107 } else /* open succeeded */
3109 #else /* MICRO || WIN32*/
3112 /* Create the "record" file, if necessary */
3113 fq_record
= fqname(RECORD
, SCOREPREFIX
, 0);
3114 fd
= macopen(fq_record
, O_RDWR
| O_CREAT
, TEXT_TYPE
);
3119 #endif /* MICRO || WIN32*/
3122 /* ---------- END SCOREBOARD CREATION ----------- */
3124 /* ---------- BEGIN PANIC/IMPOSSIBLE LOG ----------- */
3128 paniclog(type
, reason
)
3129 const char *type
; /* panic, impossible, trickery */
3130 const char *reason
; /* explanation */
3136 if (!program_state
.in_paniclog
) {
3137 program_state
.in_paniclog
= 1;
3138 lfile
= fopen_datafile(PANICLOG
, "a", TROUBLEPREFIX
);
3140 #ifdef PANICLOG_FMT2
3141 (void) fprintf(lfile
, "%ld %s: %s %s\n",
3142 ubirthday
, (plname
? plname
: "(none)"),
3145 time_t now
= getnow();
3147 char playmode
= wizard
? 'D' : discover
? 'X' : '-';
3149 (void) fprintf(lfile
, "%s %08ld %06ld %d %c: %s %s\n",
3150 version_string(buf
), yyyymmdd(now
), hhmmss(now
),
3151 uid
, playmode
, type
, reason
);
3152 #endif /* !PANICLOG_FMT2 */
3153 (void) fclose(lfile
);
3155 program_state
.in_paniclog
= 0;
3157 #endif /* PANICLOG */
3161 /* ---------- END PANIC/IMPOSSIBLE LOG ----------- */
3165 /* ---------- BEGIN INTERNAL RECOVER ----------- */
3170 int lev
, savelev
, hpid
, pltmpsiz
;
3172 struct version_info version_data
;
3174 char savename
[SAVESIZE
], errbuf
[BUFSZ
];
3175 struct savefile_info sfi
;
3176 char tmpplbuf
[PL_NSIZ
];
3178 for (lev
= 0; lev
< 256; lev
++)
3181 /* level 0 file contains:
3182 * pid of creating process (ignored here)
3183 * level number for current level of save file
3184 * name of save file nethack would have created
3189 gfd
= open_levelfile(0, errbuf
);
3191 raw_printf("%s\n", errbuf
);
3194 if (read(gfd
, (genericptr_t
) &hpid
, sizeof hpid
) != sizeof hpid
) {
3195 raw_printf("\n%s\n%s\n",
3196 "Checkpoint data incompletely written or subsequently clobbered.",
3197 "Recovery impossible.");
3198 (void) nhclose(gfd
);
3201 if (read(gfd
, (genericptr_t
) &savelev
, sizeof(savelev
))
3202 != sizeof(savelev
)) {
3204 "\nCheckpointing was not in effect for %s -- recovery impossible.\n",
3206 (void) nhclose(gfd
);
3209 if ((read(gfd
, (genericptr_t
) savename
, sizeof savename
)
3211 || (read(gfd
, (genericptr_t
) &version_data
, sizeof version_data
)
3212 != sizeof version_data
)
3213 || (read(gfd
, (genericptr_t
) &sfi
, sizeof sfi
) != sizeof sfi
)
3214 || (read(gfd
, (genericptr_t
) &pltmpsiz
, sizeof pltmpsiz
)
3215 != sizeof pltmpsiz
) || (pltmpsiz
> PL_NSIZ
)
3216 || (read(gfd
, (genericptr_t
) &tmpplbuf
, pltmpsiz
) != pltmpsiz
)) {
3217 raw_printf("\nError reading %s -- can't recover.\n", lock
);
3218 (void) nhclose(gfd
);
3222 /* save file should contain:
3226 * current level (including pets)
3227 * (non-level-based) game state
3230 set_savefile_name(TRUE
);
3231 sfd
= create_savefile();
3233 raw_printf("\nCannot recover savefile %s.\n", SAVEF
);
3234 (void) nhclose(gfd
);
3238 lfd
= open_levelfile(savelev
, errbuf
);
3240 raw_printf("\n%s\n", errbuf
);
3241 (void) nhclose(gfd
);
3242 (void) nhclose(sfd
);
3247 if (write(sfd
, (genericptr_t
) &version_data
, sizeof version_data
)
3248 != sizeof version_data
) {
3249 raw_printf("\nError writing %s; recovery failed.", SAVEF
);
3250 (void) nhclose(gfd
);
3251 (void) nhclose(sfd
);
3256 if (write(sfd
, (genericptr_t
) &sfi
, sizeof sfi
) != sizeof sfi
) {
3257 raw_printf("\nError writing %s; recovery failed (savefile_info).\n",
3259 (void) nhclose(gfd
);
3260 (void) nhclose(sfd
);
3265 if (write(sfd
, (genericptr_t
) &pltmpsiz
, sizeof pltmpsiz
)
3266 != sizeof pltmpsiz
) {
3267 raw_printf("Error writing %s; recovery failed (player name size).\n",
3269 (void) nhclose(gfd
);
3270 (void) nhclose(sfd
);
3275 if (write(sfd
, (genericptr_t
) &tmpplbuf
, pltmpsiz
) != pltmpsiz
) {
3276 raw_printf("Error writing %s; recovery failed (player name).\n",
3278 (void) nhclose(gfd
);
3279 (void) nhclose(sfd
);
3284 if (!copy_bytes(lfd
, sfd
)) {
3285 (void) nhclose(lfd
);
3286 (void) nhclose(sfd
);
3290 (void) nhclose(lfd
);
3291 processed
[savelev
] = 1;
3293 if (!copy_bytes(gfd
, sfd
)) {
3294 (void) nhclose(lfd
);
3295 (void) nhclose(sfd
);
3299 (void) nhclose(gfd
);
3302 for (lev
= 1; lev
< 256; lev
++) {
3303 /* level numbers are kept in xchars in save.c, so the
3304 * maximum level number (for the endlevel) must be < 256
3306 if (lev
!= savelev
) {
3307 lfd
= open_levelfile(lev
, (char *) 0);
3309 /* any or all of these may not exist */
3311 write(sfd
, (genericptr_t
) &levc
, sizeof(levc
));
3312 if (!copy_bytes(lfd
, sfd
)) {
3313 (void) nhclose(lfd
);
3314 (void) nhclose(sfd
);
3318 (void) nhclose(lfd
);
3323 (void) nhclose(sfd
);
3325 #ifdef HOLD_LOCKFILE_OPEN
3329 * We have a successful savefile!
3330 * Only now do we erase the level files.
3332 for (lev
= 0; lev
< 256; lev
++) {
3333 if (processed
[lev
]) {
3334 const char *fq_lock
;
3335 set_levelfile_name(lock
, lev
);
3336 fq_lock
= fqname(lock
, LEVELPREFIX
, 3);
3337 (void) unlink(fq_lock
);
3344 copy_bytes(ifd
, ofd
)
3351 nfrom
= read(ifd
, buf
, BUFSIZ
);
3352 nto
= write(ofd
, buf
, nfrom
);
3355 } while (nfrom
== BUFSIZ
);
3359 /* ---------- END INTERNAL RECOVER ----------- */
3360 #endif /*SELF_RECOVER*/
3362 /* ---------- OTHER ----------- */
3372 * All we really care about is the end result - can we read the file?
3373 * So just check that directly.
3375 * Not tested on most of the old platforms (which don't attempt
3376 * to implement SYSCF).
3377 * Some ports don't like open()'s optional third argument;
3378 * VMS overrides open() usage with a macro which requires it.
3381 # if defined(NOCWD_ASSUMPTIONS) && defined(WIN32)
3382 fd
= open(fqname(SYSCF_FILE
, SYSCONFPREFIX
, 0), O_RDONLY
);
3384 fd
= open(SYSCF_FILE
, O_RDONLY
);
3387 fd
= open(SYSCF_FILE
, O_RDONLY
, 0);
3394 raw_printf("Unable to open SYSCF_FILE.\n");
3398 #endif /* SYSCF_FILE */
3402 /* used by debugpline() to decide whether to issue a message
3403 * from a particular source file; caller passes __FILE__ and we check
3404 * whether it is in the source file list supplied by SYSCF's DEBUGFILES
3406 * pass FALSE to override wildcard matching; useful for files
3407 * like dungeon.c and questpgr.c, which generate a ridiculous amount of
3408 * output if DEBUG is defined and effectively block the use of a wildcard */
3410 debugcore(filename
, wildcards
)
3411 const char *filename
;
3414 const char *debugfiles
, *p
;
3416 if (!filename
|| !*filename
)
3417 return FALSE
; /* sanity precaution */
3419 if (sysopt
.env_dbgfl
== 0) {
3420 /* check once for DEBUGFILES in the environment;
3421 if found, it supersedes the sysconf value
3422 [note: getenv() rather than nh_getenv() since a long value
3423 is valid and doesn't pose any sort of overflow risk here] */
3424 if ((p
= getenv("DEBUGFILES")) != 0) {
3425 if (sysopt
.debugfiles
)
3426 free((genericptr_t
) sysopt
.debugfiles
);
3427 sysopt
.debugfiles
= dupstr(p
);
3428 sysopt
.env_dbgfl
= 1;
3430 sysopt
.env_dbgfl
= -1;
3433 debugfiles
= sysopt
.debugfiles
;
3434 /* usual case: sysopt.debugfiles will be empty */
3435 if (!debugfiles
|| !*debugfiles
)
3438 /* strip filename's path if present */
3440 if ((p
= rindex(filename
, '/')) != 0)
3444 filename
= vms_basename(filename
);
3445 /* vms_basename strips off 'type' suffix as well as path and version;
3446 we want to put suffix back (".c" assumed); since it always returns
3447 a pointer to a static buffer, we can safely modify its result */
3448 Strcat((char *) filename
, ".c");
3452 * Wildcard match will only work if there's a single pattern (which
3453 * might be a single file name without any wildcarding) rather than
3454 * a space-separated list.
3455 * [to NOT do: We could step through the space-separated list and
3456 * attempt a wildcard match against each element, but that would be
3457 * overkill for the intended usage.]
3459 if (wildcards
&& pmatch(debugfiles
, filename
))
3462 /* check whether filename is an element of the list */
3463 if ((p
= strstr(debugfiles
, filename
)) != 0) {
3464 int l
= (int) strlen(filename
);
3466 if ((p
== debugfiles
|| p
[-1] == ' ' || p
[-1] == '/')
3467 && (p
[l
] == ' ' || p
[l
] == '\0'))
3475 /* ---------- BEGIN TRIBUTE ----------- */
3480 #define SECTIONSCOPE 1
3481 #define TITLESCOPE 2
3482 #define PASSAGESCOPE 3
3484 #define MAXPASSAGES SIZE(context.novel.pasg) /* 20 */
3486 static int FDECL(choose_passage
, (int, unsigned));
3488 /* choose a random passage that hasn't been chosen yet; once all have
3489 been chosen, reset the tracking to make all passages available again */
3491 choose_passage(passagecnt
, oid
)
3492 int passagecnt
; /* total of available passages */
3493 unsigned oid
; /* book.o_id, used to determine whether re-reading same book */
3500 /* if a different book or we've used up all the passages already,
3501 reset in order to have all 'passagecnt' passages available */
3502 if (oid
!= context
.novel
.id
|| context
.novel
.count
== 0) {
3503 int i
, range
= passagecnt
, limit
= MAXPASSAGES
;
3505 context
.novel
.id
= oid
;
3506 if (range
<= limit
) {
3507 /* collect all of the N indices */
3508 context
.novel
.count
= passagecnt
;
3509 for (idx
= 0; idx
< MAXPASSAGES
; idx
++)
3510 context
.novel
.pasg
[idx
] = (xchar
) ((idx
< passagecnt
)
3513 /* collect MAXPASSAGES of the N indices */
3514 context
.novel
.count
= MAXPASSAGES
;
3515 for (idx
= i
= 0; i
< passagecnt
; ++i
, --range
)
3516 if (range
> 0 && rn2(range
) < limit
) {
3517 context
.novel
.pasg
[idx
++] = (xchar
) (i
+ 1);
3523 idx
= rn2(context
.novel
.count
);
3524 res
= (int) context
.novel
.pasg
[idx
];
3525 /* move the last slot's passage index into the slot just used
3526 and reduce the number of passages available */
3527 context
.novel
.pasg
[idx
] = context
.novel
.pasg
[--context
.novel
.count
];
3531 /* Returns True if you were able to read something. */
3533 read_tribute(tribsection
, tribtitle
, tribpassage
, nowin_buf
, bufsz
, oid
)
3534 const char *tribsection
, *tribtitle
;
3535 int tribpassage
, bufsz
;
3537 unsigned oid
; /* book identifier */
3540 char line
[BUFSZ
], lastline
[BUFSZ
];
3543 int linect
= 0, passagecnt
= 0, targetpassage
= 0;
3544 const char *badtranslation
= "an incomprehensible foreign translation";
3545 boolean matchedsection
= FALSE
, matchedtitle
= FALSE
;
3546 winid tribwin
= WIN_ERR
;
3547 boolean grasped
= FALSE
;
3548 boolean foundpassage
= FALSE
;
3553 /* check for mandatories */
3554 if (!tribsection
|| !tribtitle
) {
3556 pline("It's %s of \"%s\"!", badtranslation
, tribtitle
);
3560 debugpline3("read_tribute %s, %s, %d.", tribsection
, tribtitle
,
3563 fp
= dlb_fopen(TRIBUTEFILE
, "r");
3565 /* this is actually an error - cannot open tribute file! */
3567 pline("You feel too overwhelmed to continue!");
3572 * Syntax (not case-sensitive):
3575 * In the books section:
3576 * %title booktitle (n)
3577 * where booktitle=book title without quotes
3578 * (n)= total number of passages present for this title
3580 * where k=sequential passage number
3582 * %e ends the passage/book/section
3583 * If in a passage, it marks the end of that passage.
3584 * If in a book, it marks the end of that book.
3585 * If in a section, it marks the end of that section.
3590 *line
= *lastline
= '\0';
3591 while (dlb_fgets(line
, sizeof line
, fp
) != 0) {
3593 (void) strip_newline(line
);
3596 if (!strncmpi(&line
[1], "section ", sizeof "section " - 1)) {
3597 char *st
= &line
[9]; /* 9 from "%section " */
3599 scope
= SECTIONSCOPE
;
3600 matchedsection
= !strcmpi(st
, tribsection
) ? TRUE
: FALSE
;
3601 } else if (!strncmpi(&line
[1], "title ", sizeof "title " - 1)) {
3602 char *st
= &line
[7]; /* 7 from "%title " */
3605 if ((p1
= index(st
, '(')) != 0) {
3607 (void) mungspaces(st
);
3608 if ((p2
= index(p1
, ')')) != 0) {
3610 passagecnt
= atoi(p1
);
3612 if (matchedsection
&& !strcmpi(st
, tribtitle
)) {
3613 matchedtitle
= TRUE
;
3614 targetpassage
= !tribpassage
3615 ? choose_passage(passagecnt
, oid
)
3616 : (tribpassage
<= passagecnt
)
3619 matchedtitle
= FALSE
;
3623 } else if (!strncmpi(&line
[1], "passage ",
3624 sizeof "passage " - 1)) {
3626 char *st
= &line
[9]; /* 9 from "%passage " */
3629 passagenum
= atoi(st
);
3630 if (passagenum
> 0 && passagenum
<= passagecnt
) {
3631 scope
= PASSAGESCOPE
;
3632 if (matchedtitle
&& passagenum
== targetpassage
) {
3633 foundpassage
= TRUE
;
3635 tribwin
= create_nhwindow(NHW_MENU
);
3636 if (tribwin
== WIN_ERR
)
3641 } else if (!strncmpi(&line
[1], "e ", sizeof "e " - 1)) {
3644 if (scope
== TITLESCOPE
)
3645 matchedtitle
= FALSE
;
3646 if (scope
== SECTIONSCOPE
)
3647 matchedsection
= FALSE
;
3651 debugpline1("tribute file error: bad %% command, line %d.",
3656 /* comment only, next! */
3661 /* outputting multi-line passage to text window */
3662 putstr(tribwin
, 0, line
);
3664 Strcpy(lastline
, line
);
3666 /* fetching one-line passage into buffer */
3667 copynchars(nowin_buf
, line
, bufsz
- 1);
3668 goto cleanup
; /* don't wait for "%e passage" */
3675 (void) dlb_fclose(fp
);
3677 /* one-line buffer */
3678 grasped
= *nowin_buf
? TRUE
: FALSE
;
3680 if (tribwin
!= WIN_ERR
) { /* implies 'foundpassage' */
3681 /* multi-line window, normal case;
3682 if lastline is empty, there were no non-empty lines between
3683 "%passage n" and "%e passage" so we leave 'grasped' False */
3685 display_nhwindow(tribwin
, FALSE
);
3686 /* put the final attribution line into message history,
3687 analogous to the summary line from long quest messages */
3688 if (index(lastline
, '['))
3689 mungspaces(lastline
); /* to remove leading spaces */
3690 else /* construct one if necessary */
3691 Sprintf(lastline
, "[%s, by Terry Pratchett]", tribtitle
);
3692 putmsghistory(lastline
, FALSE
);
3695 destroy_nhwindow(tribwin
);
3698 /* multi-line window, problem */
3699 pline("It seems to be %s of \"%s\"!", badtranslation
, tribtitle
);
3705 Death_quote(buf
, bufsz
)
3709 unsigned death_oid
= 1; /* chance of oid #1 being a novel is negligible */
3711 return read_tribute("Death", "Death Quotes", 0, buf
, bufsz
, death_oid
);
3714 /* ---------- END TRIBUTE ----------- */