1 /* SCCS Id: @(#)files.c 3.4 2003/11/14 */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
8 /* WAC for config file */
10 /* needs to be after hack.h. Caused .slashemrc to never be read on UNIX */
12 #if defined(WHEREIS_FILE) && defined(UNIX)
13 #include <sys/types.h> /* whereis-file chmod() */
17 #include "wintty.h" /* more() */
20 #if defined(GL_GRAPHICS) || defined(SDL_GRAPHICS)
21 #include "winGL.h" /* Sdlgl_parse_options */
25 #include "winproxy.h" /* proxy_config_open() */
30 #if (!defined(MAC) && !defined(O_WRONLY) && !defined(AZTEC_C)) || defined(USE_FCNTL)
35 #ifdef _MSC_VER /* MSC 6.0 defines errno quite differently */
36 # if (_MSC_VER >= 600)
51 #if defined(UNIX) && defined(QT_GRAPHICS)
55 #if defined(UNIX) || defined(VMS)
67 /* ALI: For compatibility */
69 #define compress(file) compress_area(NULL, file)
70 #define uncompress(file) uncompress_area(NULL, file)
73 #if defined(MSDOS) || defined(OS2) || defined(TOS) || defined(WIN32)
82 /* metanite64: the last else clause was added bc newer macOS versions weren't getting the include at all */
83 #ifndef O_BINARY /* used for micros, no-op for others */
87 #ifdef PREFIXES_IN_USE
89 static char fqn_filename_buffer
[FQN_NUMBUF
][FQN_MAX_FILENAME
];
92 #define BONESNAMELEN 20 /* long enough for "bon" + bonedunlvl + filecode + dlevel + ubirthday */
94 #if !defined(MFLOPPY) && !defined(VMS) && !defined(WIN32)
95 char bones
[BONESNAMELEN
] = "bonesnn.xxx";
96 char lock
[PL_NSIZ
+14] = "1lock"; /* long enough for uid+name+.99 */
99 char bones
[FILENAMELEN
]; /* pathname of bones files */
100 char lock
[FILENAMELEN
]; /* pathname of level files */
103 char bones
[BONESNAMELEN
] = "bonesnn.xxx;1";
104 char lock
[PL_NSIZ
+17] = "1lock"; /* long enough for _uid+name+.99;1 */
107 char bones
[BONESNAMELEN
] = "bonesnn.xxx";
108 char lock
[PL_NSIZ
+25]; /* long enough for username+-+name+.99 */
112 #if defined(UNIX) || defined(__BEOS__)
113 #define SAVESIZE (PL_NSIZ + 13) /* save/99999player.e */
116 #define SAVESIZE (PL_NSIZ + 22) /* [.save]<uid>player.e;1 */
119 #define SAVESIZE (PL_NSIZ + 40) /* username-player.NetHack-saved-game */
121 #define SAVESIZE FILENAMELEN /* from macconf.h or pcconf.h */
126 char SAVEF
[SAVESIZE
]; /* holds relative path of save file from playground */
128 char SAVEP
[SAVESIZE
]; /* holds path of directory for save file */
131 #ifdef HOLD_LOCKFILE_OPEN
132 struct level_ftrack
{
134 int fd
; /* file descriptor for level file */
135 int oflag
; /* open flags */
136 boolean nethack_thinks_it_is_open
; /* Does NetHack think it's open? */
141 #endif /*HOLD_LOCKFILE_OPEN*/
144 #define WIZKIT_MAX 128
145 static char wizkit
[WIZKIT_MAX
];
146 STATIC_DCL
FILE *fopen_wizkit_file(void);
150 extern char PATH
[]; /* see sys/amiga/amidos.c */
151 extern char bbs_id
[];
154 #include <proto/dos.h>
157 #include <libraries/dos.h>
158 extern void amii_set_text_font( char *, int );
161 #if defined(WIN32) || defined(MSDOS)
164 # define Delay(a) msleep(a)
168 # define DeleteFile unlink
173 # define unlink macunlink
177 extern char *sounddir
;
180 extern int n_dgns
; /* from dungeon.c */
182 STATIC_DCL
char *set_bonesfile_name(char *,d_level
*);
183 STATIC_DCL
char *set_bonestemp_name(void);
185 STATIC_DCL
void redirect(const char *,const char *,const char *,
187 STATIC_DCL
void docompress_file(const char *,const char *,BOOLEAN_P
);
191 STATIC_DCL
char *make_lockname(const char *,char *);
194 STATIC_DCL
FILE *fopen_config_file(const char *);
195 STATIC_DCL
int get_uchars(FILE *,char *,char *,uchar
*,BOOLEAN_P
,int,const char *);
196 int parse_config_line(FILE *,char *,char *,char *);
197 #ifdef NOCWD_ASSUMPTIONS
198 STATIC_DCL
void adjust_prefix(char *, int);
201 STATIC_DCL boolean
copy_bytes(int, int);
203 #ifdef HOLD_LOCKFILE_OPEN
204 STATIC_DCL
int open_levelfile_exclusively(const char *, int, int);
211 * legal zero-terminated list of acceptable file name characters
212 * quotechar lead-in character used to quote illegal characters as hex digits
214 * callerbuf buffer to house result
215 * bufsz size of callerbuf
218 * The hex digits 0-9 and A-F are always part of the legal set due to
219 * their use in the encoding scheme, even if not explicitly included in 'legal'.
222 * The following call:
223 * (void)fname_encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
224 * '%', "This is a % test!", buf, 512);
225 * results in this encoding:
226 * "This%20is%20a%20%25%20test%21"
229 fname_encode(legal
, quotechar
, s
, callerbuf
, bufsz
)
237 static char hexdigits
[] = "0123456789ABCDEF";
244 /* Do we have room for one more character or encoding? */
245 if ((bufsz
- cnt
) <= 4) return callerbuf
;
247 if (*sp
== quotechar
) {
248 (void)sprintf(op
, "%c%02X", quotechar
, *sp
);
251 } else if ((index(legal
, *sp
) != 0) || (index(hexdigits
, *sp
) != 0)) {
256 (void)sprintf(op
,"%c%02X", quotechar
, *sp
);
269 * quotechar lead-in character used to quote illegal characters as hex digits
271 * callerbuf buffer to house result
272 * bufsz size of callerbuf
275 fname_decode(quotechar
, s
, callerbuf
, bufsz
)
282 static char hexdigits
[] = "0123456789ABCDEF";
290 /* Do we have room for one more character? */
291 if ((bufsz
- cnt
) <= 2) return callerbuf
;
292 if (*sp
== quotechar
) {
294 for (k
=0; k
< 16; ++k
) if (*sp
== hexdigits
[k
]) break;
295 if (k
>= 16) return callerbuf
; /* impossible, so bail */
298 for (k
=0; k
< 16; ++k
) if (*sp
== hexdigits
[k
]) break;
299 if (k
>= 16) return callerbuf
; /* impossible, so bail */
313 #ifndef PREFIXES_IN_USE
317 fqname(basename
, whichprefix
, buffnum
)
318 const char *basename
;
319 int whichprefix
, buffnum
;
321 #ifndef PREFIXES_IN_USE
324 if (!basename
|| whichprefix
< 0 || whichprefix
>= PREFIX_COUNT
)
326 if (!fqn_prefix
[whichprefix
])
328 if (buffnum
< 0 || buffnum
>= FQN_NUMBUF
) {
329 impossible("Invalid fqn_filename_buffer specified: %d",
333 if (strlen(fqn_prefix
[whichprefix
]) + strlen(basename
) >=
335 impossible("fqname too long: %s + %s", fqn_prefix
[whichprefix
],
337 return basename
; /* XXX */
339 strcpy(fqn_filename_buffer
[buffnum
], fqn_prefix
[whichprefix
]);
340 return strcat(fqn_filename_buffer
[buffnum
], basename
);
344 /* reasonbuf must be at least BUFSZ, supplied by caller */
347 validate_prefix_locations(reasonbuf
)
350 #if defined(NOCWD_ASSUMPTIONS)
352 const char *filename
;
353 int prefcnt
, failcount
= 0;
354 char panicbuf1
[BUFSZ
], panicbuf2
[BUFSZ
], *details
;
356 if (reasonbuf
) reasonbuf
[0] = '\0';
357 for (prefcnt
= 1; prefcnt
< PREFIX_COUNT
; prefcnt
++) {
358 /* don't test writing to configdir or datadir; they're readonly */
359 if (prefcnt
== CONFIGPREFIX
|| prefcnt
== DATAPREFIX
) continue;
360 filename
= fqname("validate", prefcnt
, 3);
361 if ((fp
= fopen(filename
, "w"))) {
363 (void) unlink(filename
);
366 if (failcount
) strcat(reasonbuf
,", ");
367 strcat(reasonbuf
, fqn_prefix_names
[prefcnt
]);
369 /* the paniclog entry gets the value of errno as well */
370 sprintf(panicbuf1
,"Invalid %s", fqn_prefix_names
[prefcnt
]);
371 #if defined (NHSTDC) && !defined(NOTSTDC)
372 if (!(details
= strerror(errno
)))
375 sprintf(panicbuf2
,"\"%s\", (%d) %s",
376 fqn_prefix
[prefcnt
], errno
, details
);
377 paniclog(panicbuf1
, panicbuf2
);
389 * When file areas are in use, fopen_datafile_area is used instead.
393 /* fopen a file, with OS-dependent bells and whistles */
394 /* NOTE: a simpler version of this routine also exists in util/dlb_main.c */
396 fopen_datafile(filename
, mode
, prefix
)
397 const char *filename
, *mode
;
402 filename
= fqname(filename
, prefix
, prefix
== TROUBLEPREFIX
? 3 : 0);
403 #ifdef VMS /* essential to have punctuation, to avoid logical names */
407 if (!index(filename
, '.') && !index(filename
, ';'))
408 filename
= strcat(strcpy(tmp
, filename
), ";0");
409 fp
= fopen(filename
, mode
, "mbc=16");
412 fp
= fopen(filename
, mode
);
416 #endif /* FILE_AREA */
418 /* ---------- BEGIN LEVEL FILE HANDLING ----------- */
421 /* Set names for bones[] and lock[] */
426 strcpy(levels
, permbones
);
427 strcpy(bones
, permbones
);
429 append_slash(permbones
);
430 append_slash(levels
);
432 strncat(levels
, bbs_id
, PATHLEN
);
435 strcat(bones
, "bonesnn.*");
436 strcpy(lock
, levels
);
438 strcat(lock
, alllevels
);
445 /* Construct a file name for a level-type file, which is of the form
446 * something.level (with any old level stripped off).
447 * This assumes there is space on the end of 'file' to append
448 * a two digit number. This is true for 'level'
449 * but be careful if you use it for other things -dgk
452 set_levelfile_name(file
, lev
)
458 tf
= rindex(file
, '.');
459 if (!tf
) tf
= eos(file
);
460 sprintf(tf
, ".%d", lev
);
468 create_levelfile(lev
, errbuf
)
477 if (errbuf
) *errbuf
= '\0';
478 set_levelfile_name(lock
, lev
);
480 fq_lock
= fqname(lock
, LEVELPREFIX
, 0);
483 #if defined(MICRO) || defined(WIN32)
484 /* Use O_TRUNC to force the file to be shortened if it already
485 * exists and is currently longer.
488 fd
= open_area(FILE_AREA_LEVL
, lock
,
489 O_WRONLY
|O_CREAT
| O_TRUNC
| O_BINARY
, FCMASK
);
491 # ifdef HOLD_LOCKFILE_OPEN
493 fd
= open_levelfile_exclusively(fq_lock
, lev
,
494 O_WRONLY
|O_CREAT
| O_TRUNC
| O_BINARY
);
497 fd
= open(fq_lock
, O_WRONLY
|O_CREAT
| O_TRUNC
| O_BINARY
, FCMASK
);
501 fd
= creat_area(FILE_AREA_LEVL
, lock
, FCMASK
);
504 fd
= maccreat(fq_lock
, LEVL_TYPE
);
506 fd
= creat(fq_lock
, FCMASK
);
508 # endif /* FILE_AREAS */
509 #endif /* MICRO || WIN32 */
512 level_info
[lev
].flags
|= LFILE_EXISTS
;
513 else if (errbuf
) /* failure explanation */
515 "Cannot create file \"%s\" for level %d (errno %d).",
523 open_levelfile(lev
, errbuf
)
532 if (errbuf
) *errbuf
= '\0';
533 set_levelfile_name(lock
, lev
);
535 fq_lock
= fqname(lock
, LEVELPREFIX
, 0);
538 /* If not currently accessible, swap it in. */
539 if (level_info
[lev
].where
!= ACTIVE
)
543 fd
= open_area(FILE_AREA_LEVL
, lock
, O_RDONLY
| O_BINARY
, 0);
546 fd
= macopen(fq_lock
, O_RDONLY
| O_BINARY
, LEVL_TYPE
);
548 # ifdef HOLD_LOCKFILE_OPEN
550 fd
= open_levelfile_exclusively(fq_lock
, lev
, O_RDONLY
| O_BINARY
);
553 fd
= open(fq_lock
, O_RDONLY
| O_BINARY
, 0);
555 #endif /* FILE_AREAS */
557 /* for failure, return an explanation that our caller can use;
558 settle for `lock' instead of `fq_lock' because the latter
559 might end up being too big for nethack's BUFSZ */
560 if (fd
< 0 && errbuf
) {
562 "Cannot open file \"%s\" for level %d (errno %d).",
564 pline("THIS IS A FATAL BUG. DO NOT TRY TO SAVE THE GAME, OR YOUR SAVEGAME FILE MAY BE CORRUPTED. On a public server the problem can sometimes be solved by using a staircase to return to a previously visited level. If nothing works, please contact the admins. --Amy");
572 delete_levelfile(lev
)
576 * Level 0 might be created by port specific code that doesn't
577 * call create_levfile(), so always assume that it exists.
579 if (lev
== 0 || (level_info
[lev
].flags
& LFILE_EXISTS
)) {
580 set_levelfile_name(lock
, lev
);
582 (void) remove_area(FILE_AREA_LEVL
, lock
, 0);
584 # ifdef HOLD_LOCKFILE_OPEN
585 if (lev
== 0) really_close();
587 (void) unlink(fqname(lock
, LEVELPREFIX
, 0));
589 level_info
[lev
].flags
&= ~LFILE_EXISTS
;
597 #ifdef HANGUPHANDLING
598 if (program_state
.preserve_locks
)
603 (void) signal(SIGINT
, SIG_IGN
);
604 #if defined(UNIX) || defined(VMS)
605 sethanguphandler((void (*)(int)) SIG_IGN
);
609 /* can't access maxledgerno() before dungeons are created -dlc */
611 for (x
= (n_dgns
? maxledgerno() : 0); x
>= 0; x
--)
612 delete_levelfile(x
); /* not all levels need be present */
618 #ifdef HOLD_LOCKFILE_OPEN
620 open_levelfile_exclusively(name
, lev
, oflag
)
629 if (lftrack
.fd
>= 0) {
630 /* check for compatible access */
631 if (lftrack
.oflag
== oflag
) {
633 reslt
= lseek(fd
, 0L, SEEK_SET
);
635 panic("open_levelfile_exclusively: lseek failed %d", errno
);
636 lftrack
.nethack_thinks_it_is_open
= TRUE
;
639 fd
= sopen(name
, oflag
,SH_DENYRW
, FCMASK
);
641 lftrack
.oflag
= oflag
;
642 lftrack
.nethack_thinks_it_is_open
= TRUE
;
645 fd
= sopen(name
, oflag
,SH_DENYRW
, FCMASK
);
647 lftrack
.oflag
= oflag
;
649 lftrack
.nethack_thinks_it_is_open
= TRUE
;
658 lftrack
.nethack_thinks_it_is_open
= FALSE
;
669 if (lftrack
.fd
== fd
) {
670 really_close(); /* close it, but reopen it to hold it */
671 fd
= open_levelfile(0, (char *)0);
672 lftrack
.nethack_thinks_it_is_open
= FALSE
;
683 /* Write out our current level and branch to name.whereis
685 * Could eventually bolt on all kinds of info, but this way
686 * at least something which wants to can scan for the games.
688 * For now this only works on Win32 and UNIX. I'm too lazy
689 * to sort out all the proper other-OS stuff.
692 /* certain nasty traps obscure the information - so we want to hide it from whereis too! --Amy */
693 if (DisplayDoesNotGo
|| TheInfoIsFucked
) {
699 char whereis_file
[255];
700 char whereis_work
[255];
702 sprintf(whereis_file
,"%s",dump_format_str(WHEREIS_FILE
));
703 sprintf(whereis_work
,
704 "player=%s%s%s%s:depth=%d:dnum=%d:dname=%s:turns=%ld:score=%ld:role=%s:race=%s:gender=%s:align=%s:amulet=0\n",
705 plalias
[0] ? plalias
: plname
,
706 plalias
[0] ? " (" : "",
707 plalias
[0] ? plname
: "",
708 plalias
[0] ? ")" : "",
711 dungeons
[u
.uz
.dnum
].dname
,
716 genders
[flags
.female
].filecode
,
717 aligns
[1-u
.ualign
.type
].filecode
);
718 fp
= fopen_datafile_area(FILE_AREA_VAR
, whereis_file
, "w", LEVELPREFIX
);
721 mode_t whereismode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IWGRP
| S_IROTH
;
722 chmod(fqname(whereis_file
, LEVELPREFIX
, 2), whereismode
);
724 fwrite(whereis_work
,strlen(whereis_work
),1,fp
);
731 /* Changed over to write out where the player last was when they
732 * left the game; including possibly 'dead' :) */
737 char whereis_file
[255];
738 /*char whereis_work[255];*/
739 sprintf(whereis_file
,"%s",dump_format_str(WHEREIS_FILE
));
740 (void) unlink(fqname(whereis_file
, LEVELPREFIX
, 2));
742 fp = fopen_datafile(whereis_file,"w",LEVELPREFIX);
744 fwrite(whereis_work,strlen(whereis_work),1,fp);
749 #endif /* WHEREIS_FILE */
751 /* ---------- END LEVEL FILE HANDLING ----------- */
754 /* ---------- BEGIN BONES FILE HANDLING ----------- */
756 /* set up "file" to be file name for retrieving bones, and return a
757 * bonesid to be read/written in the bones file.
760 set_bonesfile_name(file
, lev
)
764 static char bonesid
[75];
768 /* We need more than 26 different IDs, and the yacc scanner is being stupid. If I try to use bison to generate
769 * an updated one, it's somehow lacking a lot of stuff because the dgn_comp.y simply doesn't have many of the
770 * functions present in dgn_yacc.c, what the hell? So I can't just make a second bones id to increase the amount
771 * of ones we can have. But, apparently we can just put the dungeon branch name in the file name instead! --Amy
772 * ... but then the bones level fails to link correctly :( */
774 /* used to be: sprintf(bonesid, "%c.%s" */
776 sprintf(bonesid
, "%s.%s", /*dungeons[lev->dnum].boneid*/bonedunlvl(lev
->dnum
),
777 In_quest(lev
) ? urole
.filecode
: "0");
779 if ((sptr
= Is_special(lev
)) != 0)
780 sprintf(dptr
, ".%c", sptr
->boneid
);
782 sprintf(dptr
, ".%d", lev
->dlevel
);
783 sprintf(file
, "bon%s", bonesid
);
785 sprintf(eos(file
), ".%ld", (u
.ubirthday
% 10));
793 /* set up temporary file name for writing bones, to avoid another game's
794 * trying to read from an uncompleted bones file. we want an uncontentious
795 * name, so use one in the namespace reserved for this game's level files.
796 * (we are not reading or writing level files while writing bones files, so
797 * the same array may be used instead of copying.)
804 tf
= rindex(lock
, '.');
805 if (!tf
) tf
= eos(lock
);
814 create_bonesfile(lev
, bonesid
, errbuf
)
822 if (errbuf
) *errbuf
= '\0';
823 *bonesid
= set_bonesfile_name(bones
, lev
);
824 file
= set_bonestemp_name();
826 file
= fqname(file
, BONESPREFIX
, 0);
829 #if defined(MICRO) || defined(WIN32)
830 /* Use O_TRUNC to force the file to be shortened if it already
831 * exists and is currently longer.
834 fd
= open_area(FILE_AREA_BONES
, file
,
835 O_WRONLY
|O_CREAT
| O_TRUNC
| O_BINARY
, FCMASK
);
837 fd
= open(file
, O_WRONLY
|O_CREAT
| O_TRUNC
| O_BINARY
, FCMASK
);
841 fd
= creat_area(FILE_AREA_BONES
, file
, FCMASK
);
844 fd
= maccreat(file
, BONE_TYPE
);
846 fd
= creat(file
, FCMASK
);
848 # endif /* FILE_AREAS */
850 if (fd
< 0 && errbuf
) /* failure explanation */
852 "Cannot create bones \"%s\", id %s (errno %d).",
853 lock
, *bonesid
, errno
);
855 # if defined(VMS) && !defined(SECURE)
857 Re-protect bones file with world:read+write+execute+delete access.
858 umask() doesn't seem very reliable; also, vaxcrtl won't let us set
859 delete access without write access, which is what's really wanted.
860 Can't simply create it with the desired protection because creat
861 ANDs the mask with the user's default protection, which usually
862 denies some or all access to world.
865 (void) chmod(file
, FCMASK
| 007); /* allow other users full access */
867 (void) chmod_area(FILE_AREA_BONES
, file
, FCMASK
| 007);
869 # endif /* VMS && !SECURE */
875 /* remove partial bonesfile in process of creation */
879 const char *tempname
;
881 tempname
= set_bonestemp_name();
883 (void) remove_area(FILE_AREA_BONES
, tempname
);
885 tempname
= fqname(tempname
, BONESPREFIX
, 0);
886 (void) unlink(tempname
);
891 /* move completed bones file to proper name */
893 commit_bonesfile(lev
)
896 const char *fq_bones
, *tempname
;
899 (void) set_bonesfile_name(bones
, lev
);
901 fq_bones
= fqname(bones
, BONESPREFIX
, 0);
903 tempname
= set_bonestemp_name();
905 tempname
= fqname(tempname
, BONESPREFIX
, 1);
909 ret
= rename_area(FILE_AREA_BONES
, tempname
, bones
);
911 # if (defined(SYSV) && !defined(SVR4)) || defined(GENIX)
912 /* old SYSVs don't have rename. Some SVR3's may, but since they
913 * also have link/unlink, it doesn't matter. :-)
915 (void) unlink(fq_bones
);
916 ret
= link(tempname
, fq_bones
);
917 ret
+= unlink(tempname
);
919 ret
= rename(tempname
, fq_bones
);
921 #endif /* FILE_AREAS */
923 if (wizard
&& ret
!= 0)
925 pline("couldn't rename %s to %s.", tempname
, bones
);
927 pline("couldn't rename %s to %s.", tempname
, fq_bones
);
934 open_bonesfile(lev
, bonesid
)
938 const char *fq_bones
;
941 *bonesid
= set_bonesfile_name(bones
, lev
);
943 uncompress_area(FILE_AREA_BONES
, bones
); /* no effect if nonexistent */
944 fd
= open_area(FILE_AREA_BONES
, bones
, O_RDONLY
| O_BINARY
, 0);
946 fq_bones
= fqname(bones
, BONESPREFIX
, 0);
947 uncompress(fq_bones
); /* no effect if nonexistent */
949 fd
= macopen(fq_bones
, O_RDONLY
| O_BINARY
, BONE_TYPE
);
951 fd
= open(fq_bones
, O_RDONLY
| O_BINARY
, 0);
953 #endif /* FILE_AREAS */
959 delete_bonesfile(lev
)
962 (void) set_bonesfile_name(bones
, lev
);
964 return !(remove_area(FILE_AREA_BONES
, bones
) < 0);
966 return !(unlink(fqname(bones
, BONESPREFIX
, 0)) < 0);
971 /* assume we're compressing the recently read or created bonesfile, so the
972 * file name is already set properly */
977 compress_area(FILE_AREA_BONES
, bones
);
979 compress(fqname(bones
, BONESPREFIX
, 0));
983 /* ---------- END BONES FILE HANDLING ----------- */
986 /* ---------- BEGIN SAVE FILE HANDLING ----------- */
988 /* set savefile name in OS-dependent manner from pre-existing plname,
989 * avoiding troublesome characters */
994 char fnamebuf
[BUFSZ
], encodedfnamebuf
[BUFSZ
];
998 sprintf(SAVEF
, "[.save]%d%s", getuid(), plname
);
1001 sprintf(SAVEF
, "%d%s", getuid(), plname
);
1004 strcat(SAVEF
, ";1");
1007 strcpy(SAVEF
, SAVEP
);
1009 strncat(SAVEF
, bbs_id
, PATHLEN
);
1012 int i
= strlen(SAVEP
);
1014 /* plname has to share space with SAVEP and ".sav" */
1015 (void)strncat(SAVEF
, plname
, FILENAME
- i
- 4);
1017 (void)strncat(SAVEF
, plname
, 8);
1019 regularize(SAVEF
+i
);
1021 strcat(SAVEF
, ".sav");
1025 /* Obtain the name of the logged on user and incorporate
1026 * it into the name. */
1027 sprintf(fnamebuf
, "%s-%s", get_username(0), plname
);
1028 (void)fname_encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.",
1029 '%', fnamebuf
, encodedfnamebuf
, BUFSZ
);
1030 sprintf(SAVEF
, "%s.NetHack-saved-game", encodedfnamebuf
);
1032 sprintf(SAVEF
, "save/%d%s", (int)getuid(), plname
);
1033 regularize(SAVEF
+5); /* avoid . or / in name */
1036 sprintf(SAVEF
, "%d%s", (int)getuid(), plname
);
1037 regularize(SAVEF
); /* avoid . or / in name */
1045 save_savefile_name(fd
)
1048 (void) write(fd
, (void *) SAVEF
, sizeof(SAVEF
));
1053 #if defined(WIZARD) && !defined(MICRO)
1054 /* change pre-existing savefile name to indicate an error savefile */
1056 set_error_savefile()
1060 char *semi_colon
= rindex(SAVEF
, ';');
1061 if (semi_colon
) *semi_colon
= '\0';
1063 strcat(SAVEF
, ".e;1");
1066 strcat(SAVEF
, "-e");
1068 strcat(SAVEF
, ".e");
1075 /* create save file, overwriting one if it already exists */
1080 const char *fq_save
;
1086 fd
= open_area(FILE_AREA_SAVE
, SAVEF
,
1087 O_WRONLY
| O_BINARY
| O_CREAT
| O_TRUNC
, FCMASK
);
1089 fd
= creat_area(FILE_AREA_SAVE
, SAVEF
, FCMASK
);
1091 #else /* FILE_AREAS */
1092 fq_save
= fqname(SAVEF
, SAVEPREFIX
, 0);
1093 # if defined(MICRO) || defined(WIN32)
1094 fd
= open(fq_save
, O_WRONLY
| O_BINARY
| O_CREAT
| O_TRUNC
, FCMASK
);
1097 fd
= maccreat(fq_save
, SAVE_TYPE
);
1099 fd
= creat(fq_save
, FCMASK
);
1102 #endif /* FILE_AREAS */
1104 #if defined(VMS) && !defined(SECURE)
1106 Make sure the save file is owned by the current process. That's
1107 the default for non-privileged users, but for priv'd users the
1108 file will be owned by the directory's owner instead of the user.
1110 # ifdef getuid /*(see vmsunix.c)*/
1114 (void) chown_area(FILE_AREA_SAVE
, SAVEF
, getuid(), getgid());
1116 (void) chown(fq_save
, getuid(), getgid());
1118 #endif /* VMS && !SECURE */
1124 /* open savefile for reading */
1131 fd
= open_area(FILE_AREA_SAVE
, SAVEF
, O_RDONLY
| O_BINARY
, 0);
1133 const char *fq_save
;
1134 fq_save
= fqname(SAVEF
, SAVEPREFIX
, 0);
1136 fd
= macopen(fq_save
, O_RDONLY
| O_BINARY
, SAVE_TYPE
);
1138 fd
= open(fq_save
, O_RDONLY
| O_BINARY
, 0);
1140 #endif /* FILE_AREAS */
1145 /* delete savefile */
1149 /*WAC OK...this is probably a contreversial addition. It's an option tho*/
1151 /* Wizard mode already has prompt*/
1152 if (flags
.keep_savefile
&& !wizard
) {
1153 # ifdef AMIGA /*WAC If Amiga is ever supported*/
1156 return 1; /*Should this return 0?*/
1161 (void) remove_area(FILE_AREA_SAVE
, SAVEF
);
1163 (void) unlink(fqname(SAVEF
, SAVEPREFIX
, 0));
1165 return 0; /* for restore_saved_game() (ex-xxxmain.c) test */
1169 /* try to open up a save file and prepare to restore it */
1171 restore_saved_game()
1174 const char *fq_save
;
1178 set_savefile_name();
1180 if (!saveDiskPrompt(1))
1182 #endif /* MFLOPPY */
1184 fq_save
= fqname(SAVEF
, SAVEPREFIX
, 0);
1186 uncompress(fq_save
);
1188 uncompress_area(FILE_AREA_SAVE
, SAVEF
);
1190 if ((fd
= open_savefile()) < 0) return fd
;
1193 if (!uptodate(fd
, fq_save
)) {
1195 if (!uptodate(fd
, SAVEF
)) {
1197 (void) close(fd
), fd
= -1;
1198 if (yn("Delete the old file?") == 'y') /* Damn you, sadistic programmers who delete stuff without asking! --Amy */
1199 (void) delete_savefile();
1204 #if defined(UNIX) && defined(QT_GRAPHICS)
1207 plname_from_file(filename
)
1208 const char* filename
;
1210 #ifdef STORE_PLNAME_IN_FILE
1214 strcpy(SAVEF
,filename
);
1215 #ifdef COMPRESS_EXTENSION
1216 SAVEF
[strlen(SAVEF
)-strlen(COMPRESS_EXTENSION
)] = '\0';
1219 if ((fd
= open_savefile()) >= 0) {
1220 if (uptodate(fd
, filename
)) {
1221 char tplname
[PL_NSIZ
];
1222 mread(fd
, (void *) tplname
, PL_NSIZ
);
1223 result
= strdup(tplname
);
1231 # if defined(UNIX) && defined(QT_GRAPHICS)
1232 /* Name not stored in save file, so we have to extract it from
1233 the filename, which loses information
1234 (eg. "/", "_", and "." characters are lost. */
1237 char name
[64]; /* more than PL_NSIZ */
1238 #ifdef COMPRESS_EXTENSION
1239 #define EXTSTR COMPRESS_EXTENSION
1243 if ( sscanf( filename
, "%*[^/]/%d%63[^.]" EXTSTR
, &uid
, name
) == 2 ) {
1245 /* "_" most likely means " ", which certainly looks nicer */
1246 for (k
=0; name
[k
]; k
++)
1249 return strdup(name
);
1257 #endif /* defined(UNIX) && defined(QT_GRAPHICS) */
1262 #if defined(UNIX) && defined(QT_GRAPHICS)
1264 struct dirent
**namelist
;
1265 int n
= scandir("save", &namelist
, 0, alphasort
);;
1268 char** result
= (char**)alloc((n
+1)*sizeof(char*)); /* at most */
1269 for (i
=0; i
<n
; i
++) {
1271 char name
[64]; /* more than PL_NSIZ */
1272 if ( sscanf( namelist
[i
]->d_name
, "%d%63s", &uid
, name
) == 2 ) {
1273 if ( uid
== myuid
) {
1274 char filename
[BUFSZ
];
1276 sprintf(filename
,"save/%d%s",uid
,name
);
1277 r
= plname_from_file(filename
);
1293 free_saved_games(saved
)
1298 while (saved
[i
]) free((void *)saved
[i
++]);
1299 free((void *)saved
);
1304 /* ---------- END SAVE FILE HANDLING ----------- */
1307 /* ---------- BEGIN FILE COMPRESSION HANDLING ----------- */
1312 redirect(filearea
, filename
, mode
, stream
, uncomp
)
1313 const char *filearea
, *filename
, *mode
;
1318 if (freopen(filename
, mode
, stream
) == (FILE *)0) {
1320 if (freopen_area(filearea
, filename
, mode
, stream
) == (FILE *)0) {
1322 (void) fprintf(stderr
, "redirect of %s for %scompress failed\n",
1323 filename
, uncomp
? "un" : "");
1324 terminate(EXIT_FAILURE
);
1329 * using system() is simpler, but opens up security holes and causes
1330 * problems on at least Interactive UNIX 3.0.1 (SVR3.2), where any
1331 * setuid is renounced by /bin/sh, so the files cannot be accessed.
1333 * cf. child() in unixunix.c.
1336 docompress_file(filearea
, filename
, uncomp
)
1337 const char *filearea
, *filename
;
1342 const char *args
[10];
1343 # ifdef COMPRESS_OPTIONS
1348 # ifdef TTY_GRAPHICS
1349 boolean istty
= !strncmpi(windowprocs
.name
, "tty", 3);
1352 strcpy(cfn
, filename
);
1353 # ifdef COMPRESS_EXTENSION
1354 strcat(cfn
, COMPRESS_EXTENSION
);
1356 /* when compressing, we know the file exists */
1358 if ((cf
= fopen_datafile_area(filearea
, cfn
, RDBMODE
, FALSE
)) ==
1365 if (uncomp
) args
[++i
] = "-d"; /* uncompress */
1366 # ifdef COMPRESS_OPTIONS
1368 /* we can't guarantee there's only one additional option, sigh */
1370 boolean inword
= FALSE
;
1372 strcpy(opts
, COMPRESS_OPTIONS
);
1375 if ((*opt
== ' ') || (*opt
== '\t')) {
1380 } else if (!inword
) {
1388 args
[++i
] = (char *)0;
1390 # ifdef TTY_GRAPHICS
1391 /* If we don't do this and we are right after a y/n question *and*
1392 * there is an error message from the compression, the 'y' or 'n' can
1393 * end up being displayed after the error message.
1399 if (f
== 0) { /* child */
1400 # ifdef TTY_GRAPHICS
1401 /* any error messages from the compression must come out after
1402 * the first line, because the more() to let the user read
1403 * them will have to clear the first line. This should be
1404 * invisible if there are no error messages.
1409 /* run compressor without privileges, in case other programs
1410 * have surprises along the line of gzip once taking filenames
1413 /* assume all compressors will compress stdin to stdout
1414 * without explicit filenames. this is true of at least
1415 * compress and gzip, those mentioned in config.h.
1418 redirect(filearea
, cfn
, RDBMODE
, stdin
, uncomp
);
1419 redirect(filearea
, filename
, WRBMODE
, stdout
, uncomp
);
1421 redirect(filearea
, filename
, RDBMODE
, stdin
, uncomp
);
1422 redirect(filearea
, cfn
, WRBMODE
, stdout
, uncomp
);
1424 (void) setgid(getgid());
1425 (void) setuid(getuid());
1426 (void) execv(args
[0], (char *const *) args
);
1428 (void) fprintf(stderr
, "Exec to %scompress %s failed.\n",
1429 uncomp
? "un" : "", filename
);
1430 terminate(EXIT_FAILURE
);
1431 } else if (f
== -1) {
1433 pline("Fork to %scompress %s failed.",
1434 uncomp
? "un" : "", filename
);
1438 (void) signal(SIGINT
, SIG_IGN
);
1439 (void) signal(SIGQUIT
, SIG_IGN
);
1441 (void) wait((int *)&i
);
1443 (void) signal(SIGINT
, (SIG_RET_TYPE
) done1
);
1446 if (wizard
) (void) signal(SIGQUIT
, SIG_DFL
);
1449 /* (un)compress succeeded: remove file left behind */
1454 (void) remove_area(filearea
, cfn
);
1458 (void) unlink(filename
);
1460 (void) remove_area(filearea
, filename
);
1463 /* (un)compress failed; remove the new, bad file */
1465 raw_printf("Unable to uncompress %s", filename
);
1466 (void) unlink(filename
);
1468 /* no message needed for compress case; life will go on */
1472 /* Give them a chance to read any error messages from the
1473 * compression--these would go to stdout or stderr and would get
1474 * overwritten only in tty mode. It's still ugly, since the
1475 * messages are being written on top of the screen, but at least
1476 * the user can read them.
1480 clear_nhwindow(WIN_MESSAGE
);
1482 /* No way to know if this is feasible */
1488 #endif /* COMPRESS */
1492 compress_area(filearea
, filename
)
1493 const char *filearea
, *filename
;
1496 #if defined(MAC_MPW) || defined(__MWERKS__)
1497 # pragma unused(filename)
1500 docompress_file(filearea
, filename
, FALSE
);
1505 /* uncompress file if it exists */
1507 uncompress_area(filearea
, filename
)
1508 const char *filearea
, *filename
;
1511 #if defined(MAC_MPW) || defined(__MWERKS__)
1512 # pragma unused(filename)
1515 docompress_file(filearea
, filename
, TRUE
);
1519 /* ---------- END FILE COMPRESSION HANDLING ----------- */
1523 * When file areas are in use, (un)lock_file_area are used instead.
1528 /* ---------- BEGIN FILE LOCKING HANDLING ----------- */
1530 static int nesting
= 0;
1532 #if defined(NO_FILE_LINKS) || defined(USE_FCNTL) /* implies UNIX */
1533 static int lockfd
; /* for lock_file() to pass to unlock_file() */
1536 struct flock sflock
; /* for unlocking, same as above */
1539 #define HUP if (!program_state.done_hup)
1543 make_lockname(filename
, lockname
)
1544 const char *filename
;
1547 #if defined(MAC_MPW) || defined(__MWERKS__)
1548 # pragma unused(filename,lockname)
1551 # if defined(UNIX) || defined(VMS) || defined(AMIGA) || defined(WIN32) || defined(MSDOS)
1552 # ifdef NO_FILE_LINKS
1553 strcpy(lockname
, LOCKDIR
);
1554 strcat(lockname
, "/");
1555 strcat(lockname
, filename
);
1557 strcpy(lockname
, filename
);
1561 char *semi_colon
= rindex(lockname
, ';');
1562 if (semi_colon
) *semi_colon
= '\0';
1564 strcat(lockname
, ".lock;1");
1566 strcat(lockname
, "_lock");
1572 # endif /* UNIX || VMS || AMIGA || WIN32 || MSDOS */
1575 #endif /* !USE_FCNTL */
1580 lock_file(filename
, whichprefix
, retryct
)
1581 const char *filename
;
1585 #if defined(MAC_MPW) || defined(__MWERKS__)
1586 # pragma unused(filename, retryct)
1589 char locknambuf
[BUFSZ
];
1590 const char *lockname
;
1595 impossible("TRIED TO NEST LOCKS");
1600 lockname
= make_lockname(filename
, locknambuf
);
1601 #ifndef NO_FILE_LINKS /* LOCKDIR should be subsumed by LOCKPREFIX */
1602 lockname
= fqname(lockname
, LOCKPREFIX
, 2);
1605 filename
= fqname(filename
, whichprefix
, 0);
1608 lockfd
= open(filename
,O_RDWR
);
1610 HUP
raw_printf("Cannot open file %s. This is a program bug.",
1613 sflock
.l_type
= F_WRLCK
;
1614 sflock
.l_whence
= SEEK_SET
;
1619 #if defined(UNIX) || defined(VMS)
1621 while (fcntl(lockfd
,F_SETLK
,&sflock
) == -1) {
1623 # ifdef NO_FILE_LINKS
1624 while ((lockfd
= open(lockname
, O_RDWR
|O_CREAT
|O_EXCL
, 0666)) == -1) {
1626 while (link(filename
, lockname
) == -1) {
1633 "Waiting for release of fcntl lock on %s. (%d retries left).",
1637 HUP (void) raw_print("I give up. Sorry.");
1638 HUP
raw_printf("Some other process has an unnatural grip on %s.",
1644 register int errnosv
= errno
;
1646 switch (errnosv
) { /* George Barbanis */
1650 "Waiting for access to %s. (%d retries left).",
1652 # if defined(SYSV) || defined(ULTRIX) || defined(VMS)
1657 HUP (void) raw_print("I give up. Sorry.");
1658 HUP
raw_printf("Perhaps there is an old %s around?",
1666 HUP
raw_printf("Can't find file %s to lock!", filename
);
1670 HUP
raw_printf("No write permission to lock %s!", filename
);
1673 # ifdef VMS /* c__translate(vmsfiles.c) */
1675 /* could be misleading, but usually right */
1676 HUP
raw_printf("Can't lock %s due to directory protection.",
1682 HUP
perror(lockname
);
1684 "Cannot lock %s for unknown reason (%d).",
1689 #endif /* USE_FCNTL */
1692 #endif /* UNIX || VMS */
1694 #if (defined(AMIGA) || defined(WIN32) || defined(MSDOS)) && !defined(USE_FCNTL)
1696 #define OPENFAILURE(fd) (!fd)
1699 #define OPENFAILURE(fd) (fd < 0)
1702 while (--retryct
&& OPENFAILURE(lockptr
)) {
1703 # if defined(WIN32) && !defined(WIN_CE)
1704 lockptr
= sopen(lockname
, O_RDWR
|O_CREAT
, SH_DENYRW
, S_IWRITE
);
1706 (void)DeleteFile(lockname
); /* in case dead process was here first */
1708 lockptr
= Open(lockname
,MODE_NEWFILE
);
1710 lockptr
= open(lockname
, O_RDWR
|O_CREAT
|O_EXCL
, S_IWRITE
);
1713 if (OPENFAILURE(lockptr
)) {
1714 raw_printf("Waiting for access to %s. (%d retries left).",
1720 raw_printf("I give up. Sorry.");
1724 #endif /* AMIGA || WIN32 || MSDOS */
1729 #ifdef VMS /* for unlock_file, use the unlink() routine in vmsunix.c */
1733 # define unlink(foo) vms_unlink(foo)
1736 /* unlock file, which must be currently locked by lock_file */
1738 unlock_file(filename
)
1739 const char *filename
;
1742 char locknambuf
[BUFSZ
];
1743 const char *lockname
;
1748 sflock
.l_type
= F_UNLCK
;
1749 if (fcntl(lockfd
,F_SETLK
,&sflock
) == -1) {
1750 HUP
raw_printf("Can't remove fcntl lock on %s.", filename
);
1751 (void) close(lockfd
);
1754 lockname
= make_lockname(filename
, locknambuf
);
1755 #ifndef NO_FILE_LINKS /* LOCKDIR should be subsumed by LOCKPREFIX */
1756 lockname
= fqname(lockname
, LOCKPREFIX
, 2);
1759 #if defined(UNIX) || defined(VMS)
1760 if (unlink(lockname
) < 0)
1761 HUP
raw_printf("Can't unlink %s.", lockname
);
1762 # ifdef NO_FILE_LINKS
1763 (void) close(lockfd
);
1766 #endif /* UNIX || VMS */
1768 #if defined(AMIGA) || defined(WIN32) || defined(MSDOS)
1769 if (lockptr
) Close(lockptr
);
1770 DeleteFile(lockname
);
1772 #endif /* AMIGA || WIN32 || MSDOS */
1773 #endif /* USE_FCNTL */
1779 /* ---------- END FILE LOCKING HANDLING ----------- */
1781 #endif /* FILE_AREAS */
1783 /* ---------- BEGIN CONFIG FILE HANDLING ----------- */
1785 const char *configfile
=
1787 /* WAC This stuff is in filename.h now
1791 # if defined(MAC) || defined(__BEOS__)
1793 # if defined(MSDOS) || defined(WIN32)
1804 /* conflict with speed-dial under windows
1805 * for XXX.cnf file so support of NetHack.cnf
1806 * is for backward compatibility only.
1807 * Preferred name (and first tried) is now defaults.nh but
1808 * the game will try the old name if there
1809 * is no defaults.nh.
1811 const char *backward_compat_configfile
= "nethack.cnf";
1815 #define fopenp fopen
1819 fopen_config_file(filename
)
1820 const char *filename
;
1823 #if defined(UNIX) || defined(VMS)
1824 char tmp_config
[BUFSZ
];
1828 /* "filename" is an environment variable, so it should hang around */
1829 /* if set, it is expected to be a full path name (if relevant) */
1832 if (access(filename
, 4) == -1) {
1833 /* 4 is R_OK on newer systems */
1834 /* nasty sneaky attempt to read file through
1835 * NetHack's setuid permissions -- this is the only
1836 * place a file name may be wholly under the player's
1839 raw_printf("Access to %s denied (%d).",
1842 /* fall through to standard names */
1845 if ((fp
= fopenp(filename
, "r")) != (FILE *)0) {
1846 configfile
= filename
;
1848 #if defined(UNIX) || defined(VMS)
1850 /* access() above probably caught most problems for UNIX */
1851 raw_printf("Couldn't open requested config file %s (%d).",
1854 /* fall through to standard names */
1859 #if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32)
1860 if ((fp
= fopenp(fqname(configfile
, CONFIGPREFIX
, 0), "r"))
1864 else if ((fp
= fopenp(fqname(backward_compat_configfile
,
1865 CONFIGPREFIX
, 0), "r")) != (FILE *)0)
1869 /* constructed full path names don't need fqname() */
1871 if ((fp
= fopenp(fqname(NH_CONFIG_FILE
, CONFIGPREFIX
, 0), "r"))
1873 configfile
= NH_CONFIG_FILE
;
1876 if ((fp
= fopenp(NH_CONFIG_FILE2
, "r")) != (FILE *)0) {
1877 configfile
= index(NH_CONFIG_FILE2
, ':');
1881 configfile
= NH_CONFIG_FILE2
;
1885 envp
= nh_getenv("HOME");
1887 strcpy(tmp_config
, NH_CONFIG_FILE3
);
1889 sprintf(tmp_config
, "%s%s", envp
, NH_CONFIG_FILE3
);
1890 if ((fp
= fopenp(tmp_config
, "r")) != (FILE *)0)
1892 # else /* should be only UNIX left */
1893 envp
= nh_getenv("HOME");
1895 strcpy(tmp_config
, configfile
);
1897 sprintf(tmp_config
, "%s/%s", envp
, configfile
);
1898 if ((fp
= fopenp(tmp_config
, "r")) != (FILE *)0)
1900 # if defined(__APPLE__)
1901 /* try an alternative */
1903 sprintf(tmp_config
, "%s/%s", envp
, "Library/Preferences/NetHack Defaults");
1904 if ((fp
= fopenp(tmp_config
, "r")) != (FILE *)0)
1906 sprintf(tmp_config
, "%s/%s", envp
, "Library/Preferences/NetHack Defaults.txt");
1907 if ((fp
= fopenp(tmp_config
, "r")) != (FILE *)0)
1911 if (errno
!= ENOENT
) {
1914 /* e.g., problems when setuid NetHack can't search home
1915 * directory restricted to user */
1917 #if defined (NHSTDC) && !defined(NOTSTDC)
1918 if ((details
= strerror(errno
)) == 0)
1921 raw_printf("Couldn't open default config file %s %s(%d).",
1922 tmp_config
, details
, errno
);
1925 else if (!strncmp(windowprocs
.name
, "proxy/", 6)) {
1926 fp
= fopenp("/etc/slashem/proxy.slashemrc", "r");
1927 if (fp
!= (FILE *)0)
1929 else if (errno
!= ENOENT
) {
1930 raw_printf("Couldn't open /etc/slashem/proxy.slashemrc (%d).",
1943 * Retrieve a list of integers from a file into a uchar array.
1945 * NOTE: zeros are inserted unless modlist is TRUE, in which case the list
1946 * location is unchanged. Callers must handle zeros if modlist is FALSE.
1949 get_uchars(fp
, buf
, bufp
, list
, modlist
, size
, name
)
1950 FILE *fp
; /* input file pointer */
1951 char *buf
; /* read buffer, must be of size BUFSZ */
1952 char *bufp
; /* current pointer */
1953 uchar
*list
; /* return list */
1954 boolean modlist
; /* TRUE: list is being modified in place */
1955 int size
; /* return list size */
1956 const char *name
; /* name of option for error message */
1958 unsigned int num
= 0;
1960 boolean havenum
= FALSE
;
1964 case ' ': case '\0':
1965 case '\t': case '\n':
1967 /* if modifying in place, don't insert zeros */
1968 if (num
|| !modlist
) list
[count
] = num
;
1973 if (count
== size
|| !*bufp
) return count
;
1977 case '0': case '1': case '2': case '3':
1978 case '4': case '5': case '6': case '7':
1981 num
= num
*10 + (*bufp
-'0');
1986 if (fp
== (FILE *)0)
1989 if (!fgets(buf
, BUFSZ
, fp
)) goto gi_error
;
1990 } while (buf
[0] == '#');
1996 raw_printf("Syntax error in %s", name
);
2004 #ifdef NOCWD_ASSUMPTIONS
2006 adjust_prefix(bufp
, prefixid
)
2013 /* Backward compatibility, ignore trailing ;n */
2014 if ((ptr
= index(bufp
, ';')) != 0) *ptr
= '\0';
2015 if (strlen(bufp
) > 0) {
2016 fqn_prefix
[prefixid
] = (char *)alloc(strlen(bufp
)+2);
2017 strcpy(fqn_prefix
[prefixid
], bufp
);
2018 append_slash(fqn_prefix
[prefixid
]);
2023 #define match_varname(INP,NAM,LEN) match_optname(INP, NAM, LEN, TRUE)
2027 parse_config_line(fp
, buf
, tmp_ramdisk
, tmp_levels
)
2033 #if defined(MAC_MPW) || defined(__MWERKS__)
2034 # pragma unused(tmp_ramdisk,tmp_levels)
2037 uchar translate
[MAXPCHARS
];
2043 /* remove trailing whitespace */
2045 while (--bufp
> buf
&& isspace((int)*bufp
))
2049 return 1; /* skip all-blank lines */
2051 *(bufp
+ 1) = '\0'; /* terminate line */
2053 /* find the '=' or ':' */
2054 bufp
= index(buf
, '=');
2055 altp
= index(buf
, ':');
2056 if (!bufp
|| (altp
&& altp
< bufp
)) bufp
= altp
;
2057 if (!bufp
) return 0;
2059 /* skip whitespace between '=' and value */
2060 do { ++bufp
; } while (isspace((int)*bufp
));
2062 /* Go through possible variables */
2063 /* some of these (at least LEVELS and SAVE) should now set the
2064 * appropriate fqn_prefix[] rather than specialized variables
2066 if (match_varname(buf
, "OPTIONS", 4)) {
2067 parseoptions(bufp
, TRUE
, TRUE
);
2068 if (plname
[0]) /* If a name was given */
2069 plnamesuffix(); /* set the character class */
2070 } else if (match_varname(buf
, "TILESETS", 7)) {
2072 #ifdef AUTOPICKUP_EXCEPTIONS
2073 } else if (match_varname(buf
, "AUTOPICKUP_EXCEPTION", 5)) {
2074 add_autopickup_exception(bufp
);
2076 } else if (match_varname(buf
, "BINDINGS", 4)) {
2077 parsebindings(bufp
);
2078 } else if (match_varname(buf
, "AUTOCOMPLETE", 5)) {
2079 parseautocomplete(bufp
, TRUE
);
2080 #ifdef NOCWD_ASSUMPTIONS
2081 } else if (match_varname(buf
, "HACKDIR", 4)) {
2082 adjust_prefix(bufp
, HACKPREFIX
);
2083 } else if (match_varname(buf
, "LEVELDIR", 4) ||
2084 match_varname(buf
, "LEVELS", 4)) {
2085 adjust_prefix(bufp
, LEVELPREFIX
);
2086 } else if (match_varname(buf
, "SAVEDIR", 4)) {
2087 adjust_prefix(bufp
, SAVEPREFIX
);
2088 } else if (match_varname(buf
, "BONESDIR", 5)) {
2089 adjust_prefix(bufp
, BONESPREFIX
);
2090 } else if (match_varname(buf
, "DATADIR", 4)) {
2091 adjust_prefix(bufp
, DATAPREFIX
);
2092 } else if (match_varname(buf
, "SCOREDIR", 4)) {
2093 adjust_prefix(bufp
, SCOREPREFIX
);
2094 } else if (match_varname(buf
, "LOCKDIR", 4)) {
2095 adjust_prefix(bufp
, LOCKPREFIX
);
2096 } else if (match_varname(buf
, "CONFIGDIR", 4)) {
2097 adjust_prefix(bufp
, CONFIGPREFIX
);
2098 } else if (match_varname(buf
, "TROUBLEDIR", 4)) {
2099 adjust_prefix(bufp
, TROUBLEPREFIX
);
2100 #else /*NOCWD_ASSUMPTIONS*/
2102 } else if (match_varname(buf
, "HACKDIR", 4)) {
2103 (void) strncpy(hackdir
, bufp
, PATHLEN
-1);
2105 } else if (match_varname(buf
, "RAMDISK", 3)) {
2106 /* The following ifdef is NOT in the wrong
2107 * place. For now, we accept and silently
2110 (void) strncpy(tmp_ramdisk
, bufp
, PATHLEN
-1);
2113 } else if (match_varname(buf
, "LEVELS", 4)) {
2114 (void) strncpy(tmp_levels
, bufp
, PATHLEN
-1);
2116 } else if (match_varname(buf
, "SAVE", 4)) {
2118 extern int saveprompt
;
2121 if ((ptr
= index(bufp
, ';')) != 0) {
2124 if (*(ptr
+1) == 'n' || *(ptr
+1) == 'N') {
2131 saveprompt
= flags
.asksavedisk
;
2134 (void) strncpy(SAVEP
, bufp
, SAVESIZE
-1);
2135 append_slash(SAVEP
);
2137 #endif /*NOCWD_ASSUMPTIONS*/
2139 } else if (match_varname(buf
, "NAME", 4)) {
2140 (void) strncpy(plname
, bufp
, PL_NSIZ
-1);
2142 } else if (match_varname(buf
, "MSGTYPE", 7)) {
2145 if (sscanf(bufp
, "%10s \"%255[^\"]\"", msgtype
, pattern
) == 2) {
2146 int typ
= MSGTYP_NORMAL
;
2147 if (!strcasecmp("norep", msgtype
)) typ
= MSGTYP_NOREP
;
2148 else if (!strcasecmp("hide", msgtype
)) typ
= MSGTYP_NOSHOW
;
2149 else if (!strcasecmp("noshow", msgtype
)) typ
= MSGTYP_NOSHOW
;
2150 else if (!strcasecmp("more", msgtype
)) typ
= MSGTYP_STOP
;
2151 else if (!strcasecmp("stop", msgtype
)) typ
= MSGTYP_STOP
;
2152 if (typ
!= MSGTYP_NORMAL
) {
2153 msgpline_add(typ
, pattern
);
2156 } else if (match_varname(buf
, "ROLE", 4) ||
2157 match_varname(buf
, "CHARACTER", 4)) {
2158 if ((len
= str2role(bufp
)) >= 0)
2159 flags
.initrole
= len
;
2160 } else if (match_varname(buf
, "DOGNAME", 3)) {
2161 (void) strncpy(dogname
, bufp
, PL_PSIZ
-1);
2162 } else if (match_varname(buf
, "MONKEYNAME", 3)) {
2163 (void) strncpy(monkeyname
, bufp
, PL_PSIZ
-1);
2164 } else if (match_varname(buf
, "PARROTNAME", 3)) {
2165 (void) strncpy(parrotname
, bufp
, PL_PSIZ
-1);
2166 } else if (match_varname(buf
, "GIRLNAME", 3)) {
2167 (void) strncpy(girlname
, bufp
, PL_PSIZ
-1);
2168 } else if (match_varname(buf
, "BOYNAME", 3)) {
2169 (void) strncpy(boyname
, bufp
, PL_PSIZ
-1);
2170 } else if (match_varname(buf
, "RAVENNAME", 3)) {
2171 (void) strncpy(ravenname
, bufp
, PL_PSIZ
-1);
2172 } else if (match_varname(buf
, "DRAGONNAME", 3)) {
2173 (void) strncpy(dragonname
, bufp
, PL_PSIZ
-1);
2174 } else if (match_varname(buf
, "PLALIAS", 3)) {
2175 (void) strncpy(plalias
, bufp
, PL_NSIZ
-1);
2176 } else if (match_varname(buf
, "CATNAME", 3)) {
2177 (void) strncpy(catname
, bufp
, PL_PSIZ
-1);
2178 } else if (match_varname(buf
, "RATNAME", 3)) {
2179 (void) strncpy(ratname
, bufp
, PL_PSIZ
-1);
2180 } else if (match_varname(buf
, "WOLFNAME", 3)) {
2181 (void) strncpy(wolfname
, bufp
, PL_PSIZ
-1);
2182 } else if (match_varname(buf
, "GHOULNAME", 3)) {
2183 (void) strncpy(ghoulname
, bufp
, PL_PSIZ
-1);
2185 } else if (match_varname(buf
, "BATNAME", 3)) {
2186 (void) strncpy(batname
, bufp
, PL_PSIZ
-1);
2187 } else if (match_varname(buf
, "SNAKENAME", 3)) {
2188 (void) strncpy(batname
, bufp
, PL_PSIZ
-1);
2189 } else if (match_varname(buf
, "RATNAME", 3)) {
2190 (void) strncpy(batname
, bufp
, PL_PSIZ
-1);
2191 } else if (match_varname(buf
, "BADGERNAME", 3)) {
2192 (void) strncpy(batname
, bufp
, PL_PSIZ
-1);
2193 } else if (match_varname(buf
, "REDDRAGONNAME", 3)) {
2194 (void) strncpy(batname
, bufp
, PL_PSIZ
-1);
2195 } else if (match_varname(buf
, "WHITEDRAGONNAME", 3)) {
2196 (void) strncpy(batname
, bufp
, PL_PSIZ
-1);
2199 } else if (match_varname(buf
, "BOULDER", 3)) {
2200 (void) get_uchars(fp
, buf
, bufp
, &iflags
.bouldersym
, TRUE
,
2202 } else if (match_varname(buf
, "MENUCOLOR", 9) || match_varname(buf
, "MENUCOLOUR", 10)) {
2204 add_menu_coloring(bufp
);
2206 } else if (match_varname(buf
, "GRAPHICS", 4)) {
2207 len
= get_uchars(fp
, buf
, bufp
, translate
, FALSE
,
2208 MAXPCHARS
, "GRAPHICS");
2209 assign_graphics(translate
, len
, MAXPCHARS
, 0);
2210 #if defined(STATUS_COLORS) && defined(TEXTCOLOR)
2211 } else if (match_varname(buf
, "STATUSCOLOR", 11) || match_varname(buf
, "STATUSCOLOUR", 12)) {
2212 (void) parse_status_color_options(bufp
);
2214 } else if (match_varname(buf
, "DUNGEON", 4)) {
2215 len
= get_uchars(fp
, buf
, bufp
, translate
, FALSE
,
2216 MAXDCHARS
, "DUNGEON");
2217 assign_graphics(translate
, len
, MAXDCHARS
, 0);
2218 } else if (match_varname(buf
, "TRAPS", 4)) {
2219 len
= get_uchars(fp
, buf
, bufp
, translate
, FALSE
,
2220 MAXTCHARS
, "TRAPS");
2221 assign_graphics(translate
, len
, MAXTCHARS
, MAXDCHARS
);
2222 } else if (match_varname(buf
, "EFFECTS", 4)) {
2223 len
= get_uchars(fp
, buf
, bufp
, translate
, FALSE
,
2224 MAXECHARS
, "EFFECTS");
2225 assign_graphics(translate
, len
, MAXECHARS
, MAXDCHARS
+MAXTCHARS
);
2227 } else if (match_varname(buf
, "OBJECTS", 3)) {
2228 /* oc_syms[0] is the RANDOM object, unused */
2229 (void) get_uchars(fp
, buf
, bufp
, &(oc_syms
[1]), TRUE
,
2230 MAXOCLASSES
-1, "OBJECTS");
2231 } else if (match_varname(buf
, "MONSTERS", 3)) {
2232 /* monsyms[0] is unused */
2233 (void) get_uchars(fp
, buf
, bufp
, &(monsyms
[1]), TRUE
,
2234 MAXMCLASSES
-1, "MONSTERS");
2235 } else if (match_varname(buf
, "WARNINGS", 5)) {
2236 (void) get_uchars(fp
, buf
, bufp
, translate
, TRUE
,
2237 WARNCOUNT
, "WARNINGS");
2238 assign_warnings(translate
);
2240 } else if (match_varname(buf
, "WIZKIT", 6)) {
2241 (void) strncpy(wizkit
, bufp
, WIZKIT_MAX
-1);
2244 } else if (match_varname(buf
, "FONT", 4)) {
2247 if( t
= strchr( buf
+5, ':' ) )
2250 amii_set_text_font( buf
+5, atoi( t
+ 1 ) );
2253 } else if (match_varname(buf
, "PATH", 4)) {
2254 (void) strncpy(PATH
, bufp
, PATHLEN
-1);
2255 } else if (match_varname(buf
, "DEPTH", 5)) {
2256 extern int amii_numcolors
;
2257 int val
= atoi( bufp
);
2258 amii_numcolors
= 1L << min( DEPTH
, val
);
2259 } else if (match_varname(buf
, "DRIPENS", 7)) {
2262 for (i
= 0, t
= strtok(bufp
, ",/"); t
!= (char *)0;
2263 i
< 20 && (t
= strtok((char*)0, ",/")), ++i
) {
2264 sscanf(t
, "%d", &val
);
2265 flags
.amii_dripens
[i
] = val
;
2267 } else if (match_varname(buf
, "SCREENMODE", 10 )) {
2268 extern long amii_scrnmode
;
2269 if (!stricmp(bufp
,"req"))
2270 amii_scrnmode
= 0xffffffff; /* Requester */
2271 else if( sscanf(bufp
, "%x", &amii_scrnmode
) != 1 )
2273 } else if (match_varname(buf
, "MSGPENS", 7)) {
2274 extern int amii_msgAPen
, amii_msgBPen
;
2275 char *t
= strtok(bufp
, ",/");
2278 sscanf(t
, "%d", &amii_msgAPen
);
2279 if( t
= strtok((char*)0, ",/") )
2280 sscanf(t
, "%d", &amii_msgBPen
);
2282 } else if (match_varname(buf
, "TEXTPENS", 8)) {
2283 extern int amii_textAPen
, amii_textBPen
;
2284 char *t
= strtok(bufp
, ",/");
2287 sscanf(t
, "%d", &amii_textAPen
);
2288 if( t
= strtok((char*)0, ",/") )
2289 sscanf(t
, "%d", &amii_textBPen
);
2291 } else if (match_varname(buf
, "MENUPENS", 8)) {
2292 extern int amii_menuAPen
, amii_menuBPen
;
2293 char *t
= strtok(bufp
, ",/");
2296 sscanf(t
, "%d", &amii_menuAPen
);
2297 if( t
= strtok((char*)0, ",/") )
2298 sscanf(t
, "%d", &amii_menuBPen
);
2300 } else if (match_varname(buf
, "STATUSPENS", 10)) {
2301 extern int amii_statAPen
, amii_statBPen
;
2302 char *t
= strtok(bufp
, ",/");
2305 sscanf(t
, "%d", &amii_statAPen
);
2306 if( t
= strtok((char*)0, ",/") )
2307 sscanf(t
, "%d", &amii_statBPen
);
2309 } else if (match_varname(buf
, "OTHERPENS", 9)) {
2310 extern int amii_otherAPen
, amii_otherBPen
;
2311 char *t
= strtok(bufp
, ",/");
2314 sscanf(t
, "%d", &amii_otherAPen
);
2315 if( t
= strtok((char*)0, ",/") )
2316 sscanf(t
, "%d", &amii_otherBPen
);
2318 } else if (match_varname(buf
, "PENS", 4)) {
2319 extern unsigned short amii_init_map
[ AMII_MAXCOLORS
];
2323 for (i
= 0, t
= strtok(bufp
, ",/");
2324 i
< AMII_MAXCOLORS
&& t
!= (char *)0;
2325 t
= strtok((char *)0, ",/"), ++i
)
2327 sscanf(t
, "%hx", &amii_init_map
[i
]);
2329 amii_setpens( amii_numcolors
= i
);
2330 } else if (match_varname(buf
, "FGPENS", 6)) {
2331 extern int foreg
[ AMII_MAXCOLORS
];
2335 for (i
= 0, t
= strtok(bufp
, ",/");
2336 i
< AMII_MAXCOLORS
&& t
!= (char *)0;
2337 t
= strtok((char *)0, ",/"), ++i
)
2339 sscanf(t
, "%d", &foreg
[i
]);
2341 } else if (match_varname(buf
, "BGPENS", 6)) {
2342 extern int backg
[ AMII_MAXCOLORS
];
2346 for (i
= 0, t
= strtok(bufp
, ",/");
2347 i
< AMII_MAXCOLORS
&& t
!= (char *)0;
2348 t
= strtok((char *)0, ",/"), ++i
)
2350 sscanf(t
, "%d", &backg
[i
]);
2354 } else if (match_varname(buf
, "SOUNDDIR", 8)) {
2355 sounddir
= (char *)strdup(bufp
);
2356 } else if (match_varname(buf
, "SOUND", 5)) {
2357 add_sound_mapping(bufp
);
2360 /* These should move to wc_ options */
2361 } else if (match_varname(buf
, "QT_TILEWIDTH", 12)) {
2362 extern char *qt_tilewidth
;
2363 if (qt_tilewidth
== NULL
)
2364 qt_tilewidth
=(char *)strdup(bufp
);
2365 } else if (match_varname(buf
, "QT_TILEHEIGHT", 13)) {
2366 extern char *qt_tileheight
;
2367 if (qt_tileheight
== NULL
)
2368 qt_tileheight
=(char *)strdup(bufp
);
2369 } else if (match_varname(buf
, "QT_FONTSIZE", 11)) {
2370 extern char *qt_fontsize
;
2371 if (qt_fontsize
== NULL
)
2372 qt_fontsize
=(char *)strdup(bufp
);
2373 } else if (match_varname(buf
, "QT_COMPACT", 10)) {
2374 extern int qt_compact_mode
;
2375 qt_compact_mode
= atoi(bufp
);
2377 #if defined(GL_GRAPHICS) || defined(SDL_GRAPHICS)
2378 } else if (match_varname(buf
, "GL_OPTIONS", 10)) {
2379 Sdlgl_parse_options(bufp
, TRUE
, TRUE
);
2388 can_read_file(filename
)
2389 const char *filename
;
2391 return (access(filename
, 4) == 0);
2393 #endif /* USER_SOUNDS */
2396 read_config_file(filename
)
2397 const char *filename
;
2399 #define tmp_levels (char *)0
2400 #define tmp_ramdisk (char *)0
2402 #if defined(MICRO) || defined(WIN32)
2404 char tmp_levels
[PATHLEN
];
2408 char tmp_ramdisk
[PATHLEN
];
2415 #ifdef PROXY_GRAPHICS
2418 if (!(fp
= fopen_config_file(filename
)))
2423 if (!(fp
= fopen_config_file(filename
))) goto post_process
;
2426 #if defined(MICRO) || defined(WIN32)
2434 /* begin detection of duplicate configfile options */
2435 set_duplicate_opt_detection(1);
2437 while (fgets(buf
, 4*BUFSZ
, fp
)) {
2438 if (!parse_config_line(fp
, buf
, tmp_ramdisk
, tmp_levels
)) {
2439 raw_printf("Bad option line: \"%.50s\"", buf
);
2445 /* turn off detection of duplicate configfile options */
2446 set_duplicate_opt_detection(0);
2448 #ifdef PROXY_GRAPHICS
2451 * When acting as a proxy server, allow the client to provide
2452 * its own config file which overrides values in our config file.
2453 * Note: We don't want to warn of values being present in both
2454 * files, but we do want to warn of duplicates within each file.
2456 if (!strncmp(windowprocs
.name
, "proxy/", 6) &&
2457 (fp
= proxy_config_file_open())) {
2459 set_duplicate_opt_detection(1);
2460 while (fgets(buf
, 4*BUFSZ
, fp
)) {
2461 if (match_varname(buf
, "TILESETS", 7) ||
2462 match_varname(buf
, "HACKDIR", 4) ||
2463 match_varname(buf
, "LEVELDIR", 4) ||
2464 match_varname(buf
, "LEVELS", 4) ||
2465 match_varname(buf
, "SAVEDIR", 4) ||
2466 match_varname(buf
, "BONESDIR", 5) ||
2467 match_varname(buf
, "DATADIR", 4) ||
2468 match_varname(buf
, "SCOREDIR", 4) ||
2469 match_varname(buf
, "LOCKDIR", 4) ||
2470 match_varname(buf
, "CONFIGDIR", 4) ||
2471 match_varname(buf
, "TROUBLEDIR", 4) ||
2472 match_varname(buf
, "SOUNDDIR", 8) ||
2473 match_varname(buf
, "SOUND", 5)) {
2474 /* Quietly ignore many commands. There's no sense in
2475 * the client configuring these and some introduce
2476 * potential security breachs.
2480 if (!parse_config_line(fp
, buf
, tmp_ramdisk
, tmp_levels
)) {
2481 pline("Bad option line: \"%.50s\"", buf
);
2485 proxy_config_file_close(fp
);
2486 set_duplicate_opt_detection(0);
2489 if (!found
) goto post_process
;
2492 #if defined(MICRO) && !defined(NOCWD_ASSUMPTIONS)
2493 /* should be superseded by fqn_prefix[] */
2495 strcpy(permbones
, tmp_levels
);
2497 if (tmp_ramdisk
[0]) {
2498 strcpy(levels
, tmp_ramdisk
);
2499 if (strcmp(permbones
, levels
)) /* if not identical */
2503 strcpy(levels
, tmp_levels
);
2505 strcpy(bones
, levels
);
2506 # endif /* MFLOPPY */
2510 for(i
= 0; strlen(def_tilesets
[i
].name
); i
++) {
2511 strcpy(tilesets
[i
].name
, def_tilesets
[i
].name
);
2512 strcpy(tilesets
[i
].file
, def_tilesets
[i
].file
);
2513 tilesets
[i
].flags
= def_tilesets
[i
].flags
;
2517 if (tileset
[0] != '\0') {
2518 unsigned int len
= strlen(tileset
);
2519 for(i
= 0; i
< no_tilesets
; i
++)
2520 if (len
== strlen(tilesets
[i
].name
) &&
2521 !strncmpi(tilesets
[i
].name
, tileset
, len
))
2523 if (i
== no_tilesets
) {
2524 pline("Tileset %s not defined.", tileset
);
2528 strcpy(tileset
, tilesets
[i
].name
);
2538 #if defined(VMS) || defined(UNIX)
2539 char tmp_wizkit
[BUFSZ
];
2543 envp
= nh_getenv("WIZKIT");
2544 if (envp
&& *envp
) (void) strncpy(wizkit
, envp
, WIZKIT_MAX
- 1);
2545 if (!wizkit
[0]) return (FILE *)0;
2548 if (access(wizkit
, 4) == -1) {
2549 /* 4 is R_OK on newer systems */
2550 /* nasty sneaky attempt to read file through
2551 * NetHack's setuid permissions -- this is a
2552 * place a file name may be wholly under the player's
2555 raw_printf("Access to %s denied (%d).",
2558 /* fall through to standard names */
2561 if ((fp
= fopenp(wizkit
, "r")) != (FILE *)0) {
2563 #if defined(UNIX) || defined(VMS)
2565 /* access() above probably caught most problems for UNIX */
2566 raw_printf("Couldn't open requested config file %s (%d).",
2572 #if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32)
2573 if ((fp
= fopenp(fqname(wizkit
, CONFIGPREFIX
, 0), "r"))
2578 envp
= nh_getenv("HOME");
2580 sprintf(tmp_wizkit
, "%s%s", envp
, wizkit
);
2582 sprintf(tmp_wizkit
, "%s%s", "sys$login:", wizkit
);
2583 if ((fp
= fopenp(tmp_wizkit
, "r")) != (FILE *)0)
2585 # else /* should be only UNIX left */
2586 envp
= nh_getenv("HOME");
2588 sprintf(tmp_wizkit
, "%s/%s", envp
, wizkit
);
2589 else strcpy(tmp_wizkit
, wizkit
);
2590 if ((fp
= fopenp(tmp_wizkit
, "r")) != (FILE *)0)
2592 else if (errno
!= ENOENT
) {
2593 /* e.g., problems when setuid NetHack can't search home
2594 * directory restricted to user */
2595 raw_printf("Couldn't open default wizkit file %s (%d).",
2608 char *ep
, buf
[BUFSZ
];
2610 boolean bad_items
= FALSE
, skip
= FALSE
;
2612 if (!wizard
|| !(fp
= fopen_wizkit_file())) return;
2614 while (fgets(buf
, (int)(sizeof buf
), fp
)) {
2615 ep
= index(buf
, '\n');
2616 if (skip
) { /* in case previous line was too long */
2617 if (ep
) skip
= FALSE
; /* found newline; next line is normal */
2619 if (!ep
) skip
= TRUE
; /* newline missing; discard next fgets */
2620 else *ep
= '\0'; /* remove newline */
2623 otmp
= readobjnam(buf
, (struct obj
*)0, FALSE
, TRUE
);
2625 if (otmp
!= &zeroobj
)
2626 otmp
= addinv(otmp
);
2628 /* .60 limits output line width to 79 chars */
2629 raw_printf("Bad wizkit item: \"%.60s\"", buf
);
2643 /* ---------- END CONFIG FILE HANDLING ----------- */
2645 /* ---------- BEGIN SCOREBOARD CREATION ----------- */
2647 /* verify that we can write to the scoreboard file; if not, try to create one */
2649 check_recordfile(dir
)
2652 #if defined(MAC_MPW) || defined(__MWERKS__)
2653 # pragma unused(dir)
2657 const char *fq_record
;
2660 #if defined(UNIX) || defined(VMS)
2662 fd
= open_area(NH_RECORD_AREA
, NH_RECORD
, O_RDWR
, 0);
2664 fq_record
= fqname(NH_RECORD
, SCOREPREFIX
, 0);
2665 fd
= open(fq_record
, O_RDWR
, 0);
2669 # ifdef VMS /* must be stream-lf to use UPDATE_RECORD_IN_PLACE */
2670 if (!file_is_stmlf(fd
)) {
2672 "Warning: scoreboard file %s is not in stream_lf format",
2677 (void) close(fd
); /* NH_RECORD is accessible */
2679 } else if ((fd
= open_area(NH_RECORD_AREA
, NH_RECORD
, O_CREAT
|O_RDWR
, FCMASK
)) >= 0) {
2681 } else if ((fd
= open(fq_record
, O_CREAT
|O_RDWR
, FCMASK
)) >= 0) {
2683 (void) close(fd
); /* NH_RECORD newly created */
2684 # if defined(VMS) && !defined(SECURE)
2685 /* Re-protect NH_RECORD with world:read+write+execute+delete access. */
2687 (void) chmod_area(NH_RECORD_AREA
, NH_RECORD
, FCMASK
| 007);
2689 (void) chmod(fq_record
, FCMASK
| 007);
2691 # endif /* VMS && !SECURE */
2694 raw_printf("Warning: cannot write scoreboard file %s", NH_RECORD
);
2696 raw_printf("Warning: cannot write scoreboard file %s", fq_record
);
2700 #endif /* !UNIX && !VMS */
2701 #if defined(MICRO) || defined(WIN32)
2704 # ifdef OS2_CODEVIEW /* explicit path on opening for OS/2 */
2705 /* how does this work when there isn't an explicit path or fopenp
2706 * for later access to the file via fopen_datafile? ? */
2707 (void) strncpy(tmp
, dir
, PATHLEN
- 1);
2708 tmp
[PATHLEN
-1] = '\0';
2709 if ((strlen(tmp
) + 1 + strlen(NH_RECORD
)) < (PATHLEN
- 1)) {
2711 strcat(tmp
, NH_RECORD
);
2717 strcpy(tmp
, NH_RECORD
);
2719 fq_record
= fqname(NH_RECORD
, SCOREPREFIX
, 0);
2724 if ((fd
= open_area(NH_RECORD_AREA
, tmp
, O_RDWR
)) < 0) {
2726 if ((fd
= open(fq_record
, O_RDWR
)) < 0) {
2728 /* try to create empty record */
2729 # if defined(FILE_AREAS)
2730 if ((fd
= open_area(NH_RECORD_AREA
, tmp
, O_CREAT
|O_RDWR
,
2731 S_IREAD
|S_IWRITE
)) < 0) {
2732 # elif defined(AZTEC_C) || defined(_DCC) || (defined(__GNUC__) && defined(__AMIGA__))
2733 /* Aztec doesn't use the third argument */
2734 /* DICE doesn't like it */
2735 if ((fd
= open(fq_record
, O_CREAT
|O_RDWR
)) < 0) {
2737 if ((fd
= open(fq_record
, O_CREAT
|O_RDWR
, S_IREAD
|S_IWRITE
)) < 0) {
2739 raw_printf("Warning: cannot write record %s", tmp
);
2741 } else /* create succeeded */
2743 } else /* open succeeded */
2745 #else /* MICRO || WIN32*/
2748 /* Create the record file, if necessary */
2749 fq_record
= fqname(NH_RECORD
, SCOREPREFIX
, 0);
2750 fd
= macopen (fq_record
, O_RDWR
| O_CREAT
, LOGF_TYPE
);
2751 if (fd
!= -1) macclose (fd
);
2753 /* Create the logfile, if necessary */
2754 fq_record
= fqname(LOGFILE
, SCOREPREFIX
, 0);
2755 fd
= macopen (fq_record
, O_RDWR
| O_CREAT
, LOGF_TYPE
);
2756 if (fd
!= -1) macclose (fd
);
2759 #endif /* MICRO || WIN32*/
2762 /* ---------- END SCOREBOARD CREATION ----------- */
2764 /* ---------- BEGIN PANIC/IMPOSSIBLE LOG ----------- */
2768 paniclog(type
, reason
)
2769 const char *type
; /* panic, impossible, trickery */
2770 const char *reason
; /* explanation */
2776 if (!program_state
.in_paniclog
) {
2777 program_state
.in_paniclog
= 1;
2778 lfile
= fopen_datafile_area(LOGAREA
, PANICLOG
, "a", TROUBLEPREFIX
);
2780 (void) fprintf(lfile
, "%s %08ld: %s %s\n",
2781 version_string(buf
), yyyymmdd((time_t)0L),
2783 (void) fclose(lfile
);
2785 program_state
.in_paniclog
= 0;
2787 #endif /* PANICLOG */
2791 /* ---------- END PANIC/IMPOSSIBLE LOG ----------- */
2795 /* ---------- BEGIN INTERNAL RECOVER ----------- */
2800 int lev
, savelev
, hpid
;
2802 struct version_info version_data
;
2804 char savename
[SAVESIZE
], errbuf
[BUFSZ
];
2806 for (lev
= 0; lev
< 256; lev
++)
2809 /* level 0 file contains:
2810 * pid of creating process (ignored here)
2811 * level number for current level of save file
2812 * name of save file nethack would have created
2815 gfd
= open_levelfile(0, errbuf
);
2817 raw_printf("%s\n", errbuf
);
2820 if (read(gfd
, (void *) &hpid
, sizeof hpid
) != sizeof hpid
) {
2822 "\nCheckpoint data incompletely written or subsequently clobbered. Recovery impossible.");
2826 if (read(gfd
, (void *) &savelev
, sizeof(savelev
))
2827 != sizeof(savelev
)) {
2828 raw_printf("\nCheckpointing was not in effect for %s -- recovery impossible.\n",
2833 if ((read(gfd
, (void *) savename
, sizeof savename
)
2834 != sizeof savename
) ||
2835 (read(gfd
, (void *) &version_data
, sizeof version_data
)
2836 != sizeof version_data
)) {
2837 raw_printf("\nError reading %s -- can't recover.\n", lock
);
2842 /* save file should contain:
2844 * current level (including pets)
2845 * (non-level-based) game state
2848 set_savefile_name();
2849 sfd
= create_savefile();
2851 raw_printf("\nCannot recover savefile %s.\n", SAVEF
);
2856 lfd
= open_levelfile(savelev
, errbuf
);
2858 raw_printf("\n%s\n", errbuf
);
2865 if (write(sfd
, (void *) &version_data
, sizeof version_data
)
2866 != sizeof version_data
) {
2867 raw_printf("\nError writing %s; recovery failed.", SAVEF
);
2874 if (!copy_bytes(lfd
, sfd
)) {
2881 processed
[savelev
] = 1;
2883 if (!copy_bytes(gfd
, sfd
)) {
2892 for (lev
= 1; lev
< 256; lev
++) {
2893 /* level numbers are kept in xchars in save.c, so the
2894 * maximum level number (for the endlevel) must be < 256
2896 if (lev
!= savelev
) {
2897 lfd
= open_levelfile(lev
, (char *)0);
2899 /* any or all of these may not exist */
2901 write(sfd
, (void *) &levc
, sizeof(levc
));
2902 if (!copy_bytes(lfd
, sfd
)) {
2915 #ifdef HOLD_LOCKFILE_OPEN
2919 * We have a successful savefile!
2920 * Only now do we erase the level files.
2922 for (lev
= 0; lev
< 256; lev
++) {
2923 if (processed
[lev
]) {
2924 const char *fq_lock
;
2925 set_levelfile_name(lock
, lev
);
2926 fq_lock
= fqname(lock
, LEVELPREFIX
, 3);
2927 (void) unlink(fq_lock
);
2934 copy_bytes(ifd
, ofd
)
2941 nfrom
= read(ifd
, buf
, BUFSIZ
);
2942 nto
= write(ofd
, buf
, nfrom
);
2943 if (nto
!= nfrom
) return FALSE
;
2944 } while (nfrom
== BUFSIZ
);
2948 /* ---------- END INTERNAL RECOVER ----------- */
2949 #endif /*SELF_RECOVER*/