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. */
9 #include "wintty.h" /* more() */
14 #if !defined(MAC) && !defined(O_WRONLY) && !defined(AZTEC_C)
19 #ifdef _MSC_VER /* MSC 6.0 defines errno quite differently */
20 # if (_MSC_VER >= 600)
35 #if defined(UNIX) && defined(QT_GRAPHICS)
39 #if defined(UNIX) || defined(VMS)
43 #if defined(MSDOS) || defined(OS2) || defined(TOS) || defined(WIN32)
50 #ifndef O_BINARY /* used for micros, no-op for others */
54 #ifdef PREFIXES_IN_USE
56 static char fqn_filename_buffer[FQN_NUMBUF][FQN_MAX_FILENAME];
59 #if !defined(MFLOPPY) && !defined(VMS) && !defined(WIN32)
60 char bones[] = "bonesnn.xxx";
61 char lock[PL_NSIZ+14] = "1lock"; /* long enough for uid+name+.99 */
64 char bones[FILENAME]; /* pathname of bones files */
65 char lock[FILENAME]; /* pathname of level files */
68 char bones[] = "bonesnn.xxx;1";
69 char lock[PL_NSIZ+17] = "1lock"; /* long enough for _uid+name+.99;1 */
72 char bones[] = "bonesnn.xxx";
73 char lock[PL_NSIZ+25]; /* long enough for username+-+name+.99 */
77 #if defined(UNIX) || defined(__BEOS__)
78 #define SAVESIZE (PL_NSIZ + 13) /* save/99999player.e */
81 #define SAVESIZE (PL_NSIZ + 22) /* [.save]<uid>player.e;1 */
84 #define SAVESIZE (PL_NSIZ + 40) /* username-player.NetHack-saved-game */
86 #define SAVESIZE FILENAME /* from macconf.h or pcconf.h */
91 char SAVEF[SAVESIZE]; /* holds relative path of save file from playground */
93 char SAVEP[SAVESIZE]; /* holds path of directory for save file */
96 #ifdef HOLD_LOCKFILE_OPEN
99 int fd; /* file descriptor for level file */
100 int oflag; /* open flags */
101 boolean nethack_thinks_it_is_open; /* Does NetHack think it's open? */
106 #endif /*HOLD_LOCKFILE_OPEN*/
109 #define WIZKIT_MAX 128
110 static char wizkit[WIZKIT_MAX];
111 STATIC_DCL FILE *NDECL(fopen_wizkit_file);
115 extern char PATH[]; /* see sys/amiga/amidos.c */
116 extern char bbs_id[];
119 #include <proto/dos.h>
122 #include <libraries/dos.h>
123 extern void FDECL(amii_set_text_font, ( char *, int ));
126 #if defined(WIN32) || defined(MSDOS)
129 #define Delay(a) msleep(a)
133 #define DeleteFile unlink
138 # define unlink macunlink
142 extern char *sounddir;
145 extern int n_dgns; /* from dungeon.c */
147 STATIC_DCL char *FDECL(set_bonesfile_name, (char *,d_level*));
148 STATIC_DCL char *NDECL(set_bonestemp_name);
150 STATIC_DCL void FDECL(redirect, (const char *,const char *,FILE *,BOOLEAN_P));
151 STATIC_DCL void FDECL(docompress_file, (const char *,BOOLEAN_P));
153 STATIC_DCL char *FDECL(make_lockname, (const char *,char *));
154 STATIC_DCL FILE *FDECL(fopen_config_file, (const char *));
155 STATIC_DCL int FDECL(get_uchars, (FILE *,char *,char *,uchar *,BOOLEAN_P,int,const char *));
156 int FDECL(parse_config_line, (FILE *,char *,char *,char *));
157 #ifdef NOCWD_ASSUMPTIONS
158 STATIC_DCL void FDECL(adjust_prefix, (char *, int));
161 STATIC_DCL boolean FDECL(copy_bytes, (int, int));
163 #ifdef HOLD_LOCKFILE_OPEN
164 STATIC_DCL int FDECL(open_levelfile_exclusively, (const char *, int, int));
171 * legal zero-terminated list of acceptable file name characters
172 * quotechar lead-in character used to quote illegal characters as hex digits
174 * callerbuf buffer to house result
175 * bufsz size of callerbuf
178 * The hex digits 0-9 and A-F are always part of the legal set due to
179 * their use in the encoding scheme, even if not explicitly included in 'legal'.
182 * The following call:
183 * (void)fname_encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
184 * '%', "This is a % test!", buf, 512);
185 * results in this encoding:
186 * "This%20is%20a%20%25%20test%21"
189 fname_encode(legal, quotechar, s, callerbuf, bufsz)
197 static char hexdigits[] = "0123456789ABCDEF";
204 /* Do we have room for one more character or encoding? */
205 if ((bufsz - cnt) <= 4) return callerbuf;
207 if (*sp == quotechar) {
208 (void)sprintf(op, "%c%02X", quotechar, *sp);
211 } else if ((index(legal, *sp) != 0) || (index(hexdigits, *sp) != 0)) {
216 (void)sprintf(op,"%c%02X", quotechar, *sp);
229 * quotechar lead-in character used to quote illegal characters as hex digits
231 * callerbuf buffer to house result
232 * bufsz size of callerbuf
235 fname_decode(quotechar, s, callerbuf, bufsz)
242 static char hexdigits[] = "0123456789ABCDEF";
250 /* Do we have room for one more character? */
251 if ((bufsz - cnt) <= 2) return callerbuf;
252 if (*sp == quotechar) {
254 for (k=0; k < 16; ++k) if (*sp == hexdigits[k]) break;
255 if (k >= 16) return callerbuf; /* impossible, so bail */
258 for (k=0; k < 16; ++k) if (*sp == hexdigits[k]) break;
259 if (k >= 16) return callerbuf; /* impossible, so bail */
273 #ifndef PREFIXES_IN_USE
277 fqname(basename, whichprefix, buffnum)
278 const char *basename;
279 int whichprefix, buffnum;
281 #ifndef PREFIXES_IN_USE
284 if (!basename || whichprefix < 0 || whichprefix >= PREFIX_COUNT)
286 if (!fqn_prefix[whichprefix])
288 if (buffnum < 0 || buffnum >= FQN_NUMBUF) {
289 impossible("Invalid fqn_filename_buffer specified: %d",
293 if (strlen(fqn_prefix[whichprefix]) + strlen(basename) >=
295 impossible("fqname too long: %s + %s", fqn_prefix[whichprefix],
297 return basename; /* XXX */
299 Strcpy(fqn_filename_buffer[buffnum], fqn_prefix[whichprefix]);
300 return strcat(fqn_filename_buffer[buffnum], basename);
304 /* reasonbuf must be at least BUFSZ, supplied by caller */
307 validate_prefix_locations(reasonbuf)
310 #if defined(NOCWD_ASSUMPTIONS)
312 const char *filename;
313 int prefcnt, failcount = 0;
314 char panicbuf1[BUFSZ], panicbuf2[BUFSZ], *details;
316 if (reasonbuf) reasonbuf[0] = '\0';
317 for (prefcnt = 1; prefcnt < PREFIX_COUNT; prefcnt++) {
318 /* don't test writing to configdir or datadir; they're readonly */
319 if (prefcnt == CONFIGPREFIX || prefcnt == DATAPREFIX) continue;
320 filename = fqname("validate", prefcnt, 3);
321 if ((fp = fopen(filename, "w"))) {
323 (void) unlink(filename);
326 if (failcount) Strcat(reasonbuf,", ");
327 Strcat(reasonbuf, fqn_prefix_names[prefcnt]);
329 /* the paniclog entry gets the value of errno as well */
330 Sprintf(panicbuf1,"Invalid %s", fqn_prefix_names[prefcnt]);
331 #if defined (NHSTDC) && !defined(NOTSTDC)
332 if (!(details = strerror(errno)))
335 Sprintf(panicbuf2,"\"%s\", (%d) %s",
336 fqn_prefix[prefcnt], errno, details);
337 paniclog(panicbuf1, panicbuf2);
348 /* fopen a file, with OS-dependent bells and whistles */
349 /* NOTE: a simpler version of this routine also exists in util/dlb_main.c */
351 fopen_datafile(filename, mode, prefix)
352 const char *filename, *mode;
357 filename = fqname(filename, prefix, prefix == TROUBLEPREFIX ? 3 : 0);
358 #ifdef VMS /* essential to have punctuation, to avoid logical names */
362 if (!index(filename, '.') && !index(filename, ';'))
363 filename = strcat(strcpy(tmp, filename), ";0");
364 fp = fopen(filename, mode, "mbc=16");
367 fp = fopen(filename, mode);
372 /* ---------- BEGIN LEVEL FILE HANDLING ----------- */
375 /* Set names for bones[] and lock[] */
380 Strcpy(levels, permbones);
381 Strcpy(bones, permbones);
383 append_slash(permbones);
384 append_slash(levels);
386 strncat(levels, bbs_id, PATHLEN);
389 Strcat(bones, "bonesnn.*");
390 Strcpy(lock, levels);
392 Strcat(lock, alllevels);
399 /* Construct a file name for a level-type file, which is of the form
400 * something.level (with any old level stripped off).
401 * This assumes there is space on the end of 'file' to append
402 * a two digit number. This is true for 'level'
403 * but be careful if you use it for other things -dgk
406 set_levelfile_name(file, lev)
412 tf = rindex(file, '.');
413 if (!tf) tf = eos(file);
414 Sprintf(tf, ".%d", lev);
422 create_levelfile(lev, errbuf)
429 if (errbuf) *errbuf = '\0';
430 set_levelfile_name(lock, lev);
431 fq_lock = fqname(lock, LEVELPREFIX, 0);
433 #if defined(MICRO) || defined(WIN32)
434 /* Use O_TRUNC to force the file to be shortened if it already
435 * exists and is currently longer.
437 # ifdef HOLD_LOCKFILE_OPEN
439 fd = open_levelfile_exclusively(fq_lock, lev,
440 O_WRONLY |O_CREAT | O_TRUNC | O_BINARY);
443 fd = open(fq_lock, O_WRONLY |O_CREAT | O_TRUNC | O_BINARY, FCMASK);
446 fd = maccreat(fq_lock, LEVL_TYPE);
448 fd = creat(fq_lock, FCMASK);
450 #endif /* MICRO || WIN32 */
453 level_info[lev].flags |= LFILE_EXISTS;
454 else if (errbuf) /* failure explanation */
456 "Cannot create file \"%s\" for level %d (errno %d).",
464 open_levelfile(lev, errbuf)
471 if (errbuf) *errbuf = '\0';
472 set_levelfile_name(lock, lev);
473 fq_lock = fqname(lock, LEVELPREFIX, 0);
475 /* If not currently accessible, swap it in. */
476 if (level_info[lev].where != ACTIVE)
480 fd = macopen(fq_lock, O_RDONLY | O_BINARY, LEVL_TYPE);
482 # ifdef HOLD_LOCKFILE_OPEN
484 fd = open_levelfile_exclusively(fq_lock, lev, O_RDONLY | O_BINARY );
487 fd = open(fq_lock, O_RDONLY | O_BINARY, 0);
490 /* for failure, return an explanation that our caller can use;
491 settle for `lock' instead of `fq_lock' because the latter
492 might end up being too big for nethack's BUFSZ */
493 if (fd < 0 && errbuf)
495 "Cannot open file \"%s\" for level %d (errno %d).",
503 delete_levelfile(lev)
507 * Level 0 might be created by port specific code that doesn't
508 * call create_levfile(), so always assume that it exists.
510 if (lev == 0 || (level_info[lev].flags & LFILE_EXISTS)) {
511 set_levelfile_name(lock, lev);
512 #ifdef HOLD_LOCKFILE_OPEN
513 if (lev == 0) really_close();
515 (void) unlink(fqname(lock, LEVELPREFIX, 0));
516 level_info[lev].flags &= ~LFILE_EXISTS;
524 #if !defined(PC_LOCKING) && defined(MFLOPPY) && !defined(AMIGA)
525 eraseall(levels, alllevels);
527 eraseall(permbones, alllevels);
531 # if defined(UNIX) || defined(VMS)
532 (void) signal(SIGHUP, SIG_IGN);
534 /* can't access maxledgerno() before dungeons are created -dlc */
535 for (x = (n_dgns ? maxledgerno() : 0); x >= 0; x--)
536 delete_levelfile(x); /* not all levels need be present */
540 #ifdef HOLD_LOCKFILE_OPEN
542 open_levelfile_exclusively(name, lev, oflag)
551 if (lftrack.fd >= 0) {
552 /* check for compatible access */
553 if (lftrack.oflag == oflag) {
555 reslt = lseek(fd, 0L, SEEK_SET);
557 panic("open_levelfile_exclusively: lseek failed %d", errno);
558 lftrack.nethack_thinks_it_is_open = TRUE;
561 fd = sopen(name, oflag,SH_DENYRW, FCMASK);
563 lftrack.oflag = oflag;
564 lftrack.nethack_thinks_it_is_open = TRUE;
567 fd = sopen(name, oflag,SH_DENYRW, FCMASK);
569 lftrack.oflag = oflag;
571 lftrack.nethack_thinks_it_is_open = TRUE;
580 lftrack.nethack_thinks_it_is_open = FALSE;
591 if (lftrack.fd == fd) {
592 really_close(); /* close it, but reopen it to hold it */
593 fd = open_levelfile(0, (char *)0);
594 lftrack.nethack_thinks_it_is_open = FALSE;
601 /* ---------- END LEVEL FILE HANDLING ----------- */
604 /* ---------- BEGIN BONES FILE HANDLING ----------- */
606 /* set up "file" to be file name for retrieving bones, and return a
607 * bonesid to be read/written in the bones file.
610 set_bonesfile_name(file, lev)
617 Sprintf(file, "bon%c%s", dungeons[lev->dnum].boneid,
618 In_quest(lev) ? urole.filecode : "0");
620 if ((sptr = Is_special(lev)) != 0)
621 Sprintf(dptr, ".%c", sptr->boneid);
623 Sprintf(dptr, ".%d", lev->dlevel);
630 /* set up temporary file name for writing bones, to avoid another game's
631 * trying to read from an uncompleted bones file. we want an uncontentious
632 * name, so use one in the namespace reserved for this game's level files.
633 * (we are not reading or writing level files while writing bones files, so
634 * the same array may be used instead of copying.)
641 tf = rindex(lock, '.');
642 if (!tf) tf = eos(lock);
651 create_bonesfile(lev, bonesid, errbuf)
659 if (errbuf) *errbuf = '\0';
660 *bonesid = set_bonesfile_name(bones, lev);
661 file = set_bonestemp_name();
662 file = fqname(file, BONESPREFIX, 0);
664 #if defined(MICRO) || defined(WIN32)
665 /* Use O_TRUNC to force the file to be shortened if it already
666 * exists and is currently longer.
668 fd = open(file, O_WRONLY |O_CREAT | O_TRUNC | O_BINARY, FCMASK);
671 fd = maccreat(file, BONE_TYPE);
673 fd = creat(file, FCMASK);
676 if (fd < 0 && errbuf) /* failure explanation */
678 "Cannot create bones \"%s\", id %s (errno %d).",
679 lock, *bonesid, errno);
681 # if defined(VMS) && !defined(SECURE)
683 Re-protect bones file with world:read+write+execute+delete access.
684 umask() doesn't seem very reliable; also, vaxcrtl won't let us set
685 delete access without write access, which is what's really wanted.
686 Can't simply create it with the desired protection because creat
687 ANDs the mask with the user's default protection, which usually
688 denies some or all access to world.
690 (void) chmod(file, FCMASK | 007); /* allow other users full access */
691 # endif /* VMS && !SECURE */
697 /* remove partial bonesfile in process of creation */
701 const char *tempname;
703 tempname = set_bonestemp_name();
704 tempname = fqname(tempname, BONESPREFIX, 0);
705 (void) unlink(tempname);
709 /* move completed bones file to proper name */
711 commit_bonesfile(lev)
714 const char *fq_bones, *tempname;
717 (void) set_bonesfile_name(bones, lev);
718 fq_bones = fqname(bones, BONESPREFIX, 0);
719 tempname = set_bonestemp_name();
720 tempname = fqname(tempname, BONESPREFIX, 1);
722 #if (defined(SYSV) && !defined(SVR4)) || defined(GENIX)
723 /* old SYSVs don't have rename. Some SVR3's may, but since they
724 * also have link/unlink, it doesn't matter. :-)
726 (void) unlink(fq_bones);
727 ret = link(tempname, fq_bones);
728 ret += unlink(tempname);
730 ret = rename(tempname, fq_bones);
733 if (wizard && ret != 0)
734 pline("couldn't rename %s to %s.", tempname, fq_bones);
740 open_bonesfile(lev, bonesid)
744 const char *fq_bones;
747 *bonesid = set_bonesfile_name(bones, lev);
748 fq_bones = fqname(bones, BONESPREFIX, 0);
749 uncompress(fq_bones); /* no effect if nonexistent */
751 fd = macopen(fq_bones, O_RDONLY | O_BINARY, BONE_TYPE);
753 fd = open(fq_bones, O_RDONLY | O_BINARY, 0);
760 delete_bonesfile(lev)
763 (void) set_bonesfile_name(bones, lev);
764 return !(unlink(fqname(bones, BONESPREFIX, 0)) < 0);
768 /* assume we're compressing the recently read or created bonesfile, so the
769 * file name is already set properly */
773 compress(fqname(bones, BONESPREFIX, 0));
776 /* ---------- END BONES FILE HANDLING ----------- */
779 /* ---------- BEGIN SAVE FILE HANDLING ----------- */
781 /* set savefile name in OS-dependent manner from pre-existing plname,
782 * avoiding troublesome characters */
787 char fnamebuf[BUFSZ], encodedfnamebuf[BUFSZ];
790 Sprintf(SAVEF, "[.save]%d%s", getuid(), plname);
795 Strcpy(SAVEF, SAVEP);
797 strncat(SAVEF, bbs_id, PATHLEN);
800 int i = strlen(SAVEP);
802 /* plname has to share space with SAVEP and ".sav" */
803 (void)strncat(SAVEF, plname, FILENAME - i - 4);
805 (void)strncat(SAVEF, plname, 8);
809 Strcat(SAVEF, ".sav");
812 /* Obtain the name of the logged on user and incorporate
813 * it into the name. */
814 Sprintf(fnamebuf, "%s-%s", get_username(0), plname);
815 (void)fname_encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.",
816 '%', fnamebuf, encodedfnamebuf, BUFSZ);
817 Sprintf(SAVEF, "%s.NetHack-saved-game", encodedfnamebuf);
819 Sprintf(SAVEF, "save/%d%s", (int)getuid(), plname);
820 regularize(SAVEF+5); /* avoid . or / in name */
828 save_savefile_name(fd)
831 (void) write(fd, (genericptr_t) SAVEF, sizeof(SAVEF));
836 #if defined(WIZARD) && !defined(MICRO)
837 /* change pre-existing savefile name to indicate an error savefile */
843 char *semi_colon = rindex(SAVEF, ';');
844 if (semi_colon) *semi_colon = '\0';
846 Strcat(SAVEF, ".e;1");
858 /* create save file, overwriting one if it already exists */
865 fq_save = fqname(SAVEF, SAVEPREFIX, 0);
866 #if defined(MICRO) || defined(WIN32)
867 fd = open(fq_save, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FCMASK);
870 fd = maccreat(fq_save, SAVE_TYPE);
872 fd = creat(fq_save, FCMASK);
874 # if defined(VMS) && !defined(SECURE)
876 Make sure the save file is owned by the current process. That's
877 the default for non-privileged users, but for priv'd users the
878 file will be owned by the directory's owner instead of the user.
880 # ifdef getuid /*(see vmsunix.c)*/
883 (void) chown(fq_save, getuid(), getgid());
884 # endif /* VMS && !SECURE */
891 /* open savefile for reading */
898 fq_save = fqname(SAVEF, SAVEPREFIX, 0);
900 fd = macopen(fq_save, O_RDONLY | O_BINARY, SAVE_TYPE);
902 fd = open(fq_save, O_RDONLY | O_BINARY, 0);
908 /* delete savefile */
912 (void) unlink(fqname(SAVEF, SAVEPREFIX, 0));
913 return 0; /* for restore_saved_game() (ex-xxxmain.c) test */
917 /* try to open up a save file and prepare to restore it */
926 if (!saveDiskPrompt(1))
929 fq_save = fqname(SAVEF, SAVEPREFIX, 0);
932 if ((fd = open_savefile()) < 0) return fd;
934 if (!uptodate(fd, fq_save)) {
935 (void) close(fd), fd = -1;
936 (void) delete_savefile();
941 #if defined(UNIX) && defined(QT_GRAPHICS)
944 plname_from_file(filename)
945 const char* filename;
947 #ifdef STORE_PLNAME_IN_FILE
951 Strcpy(SAVEF,filename);
952 #ifdef COMPRESS_EXTENSION
953 SAVEF[strlen(SAVEF)-strlen(COMPRESS_EXTENSION)] = '\0';
956 if ((fd = open_savefile()) >= 0) {
957 if (uptodate(fd, filename)) {
958 char tplname[PL_NSIZ];
959 mread(fd, (genericptr_t) tplname, PL_NSIZ);
960 result = strdup(tplname);
968 # if defined(UNIX) && defined(QT_GRAPHICS)
969 /* Name not stored in save file, so we have to extract it from
970 the filename, which loses information
971 (eg. "/", "_", and "." characters are lost. */
974 char name[64]; /* more than PL_NSIZ */
975 #ifdef COMPRESS_EXTENSION
976 #define EXTSTR COMPRESS_EXTENSION
980 if ( sscanf( filename, "%*[^/]/%d%63[^.]" EXTSTR, &uid, name ) == 2 ) {
982 /* "_" most likely means " ", which certainly looks nicer */
983 for (k=0; name[k]; k++)
994 #endif /* defined(UNIX) && defined(QT_GRAPHICS) */
999 #if defined(UNIX) && defined(QT_GRAPHICS)
1001 struct dirent **namelist;
1002 int n = scandir("save", &namelist, 0, alphasort);;
1005 char** result = (char**)alloc((n+1)*sizeof(char*)); /* at most */
1006 for (i=0; i<n; i++) {
1008 char name[64]; /* more than PL_NSIZ */
1009 if ( sscanf( namelist[i]->d_name, "%d%63s", &uid, name ) == 2 ) {
1010 if ( uid == myuid ) {
1011 char filename[BUFSZ];
1013 Sprintf(filename,"save/%d%s",uid,name);
1014 r = plname_from_file(filename);
1030 free_saved_games(saved)
1035 while (saved[i]) free((genericptr_t)saved[i++]);
1036 free((genericptr_t)saved);
1041 /* ---------- END SAVE FILE HANDLING ----------- */
1044 /* ---------- BEGIN FILE COMPRESSION HANDLING ----------- */
1049 redirect(filename, mode, stream, uncomp)
1050 const char *filename, *mode;
1054 if (freopen(filename, mode, stream) == (FILE *)0) {
1055 (void) fprintf(stderr, "freopen of %s for %scompress failed\n",
1056 filename, uncomp ? "un" : "");
1057 terminate(EXIT_FAILURE);
1062 * using system() is simpler, but opens up security holes and causes
1063 * problems on at least Interactive UNIX 3.0.1 (SVR3.2), where any
1064 * setuid is renounced by /bin/sh, so the files cannot be accessed.
1066 * cf. child() in unixunix.c.
1069 docompress_file(filename, uncomp)
1070 const char *filename;
1075 const char *args[10];
1076 # ifdef COMPRESS_OPTIONS
1081 # ifdef TTY_GRAPHICS
1082 boolean istty = !strncmpi(windowprocs.name, "tty", 3);
1085 Strcpy(cfn, filename);
1086 # ifdef COMPRESS_EXTENSION
1087 Strcat(cfn, COMPRESS_EXTENSION);
1089 /* when compressing, we know the file exists */
1091 if ((cf = fopen(cfn, RDBMODE)) == (FILE *)0)
1097 if (uncomp) args[++i] = "-d"; /* uncompress */
1098 # ifdef COMPRESS_OPTIONS
1100 /* we can't guarantee there's only one additional option, sigh */
1102 boolean inword = FALSE;
1104 Strcpy(opts, COMPRESS_OPTIONS);
1107 if ((*opt == ' ') || (*opt == '\t')) {
1112 } else if (!inword) {
1120 args[++i] = (char *)0;
1122 # ifdef TTY_GRAPHICS
1123 /* If we don't do this and we are right after a y/n question *and*
1124 * there is an error message from the compression, the 'y' or 'n' can
1125 * end up being displayed after the error message.
1131 if (f == 0) { /* child */
1132 # ifdef TTY_GRAPHICS
1133 /* any error messages from the compression must come out after
1134 * the first line, because the more() to let the user read
1135 * them will have to clear the first line. This should be
1136 * invisible if there are no error messages.
1141 /* run compressor without privileges, in case other programs
1142 * have surprises along the line of gzip once taking filenames
1145 /* assume all compressors will compress stdin to stdout
1146 * without explicit filenames. this is true of at least
1147 * compress and gzip, those mentioned in config.h.
1150 redirect(cfn, RDBMODE, stdin, uncomp);
1151 redirect(filename, WRBMODE, stdout, uncomp);
1153 redirect(filename, RDBMODE, stdin, uncomp);
1154 redirect(cfn, WRBMODE, stdout, uncomp);
1156 (void) setgid(getgid());
1157 (void) setuid(getuid());
1158 (void) execv(args[0], (char *const *) args);
1160 (void) fprintf(stderr, "Exec to %scompress %s failed.\n",
1161 uncomp ? "un" : "", filename);
1162 terminate(EXIT_FAILURE);
1163 } else if (f == -1) {
1165 pline("Fork to %scompress %s failed.",
1166 uncomp ? "un" : "", filename);
1169 (void) signal(SIGINT, SIG_IGN);
1170 (void) signal(SIGQUIT, SIG_IGN);
1171 (void) wait((int *)&i);
1172 (void) signal(SIGINT, (SIG_RET_TYPE) done1);
1174 if (wizard) (void) signal(SIGQUIT, SIG_DFL);
1177 /* (un)compress succeeded: remove file left behind */
1181 (void) unlink(filename);
1183 /* (un)compress failed; remove the new, bad file */
1185 raw_printf("Unable to uncompress %s", filename);
1186 (void) unlink(filename);
1188 /* no message needed for compress case; life will go on */
1192 /* Give them a chance to read any error messages from the
1193 * compression--these would go to stdout or stderr and would get
1194 * overwritten only in tty mode. It's still ugly, since the
1195 * messages are being written on top of the screen, but at least
1196 * the user can read them.
1200 clear_nhwindow(WIN_MESSAGE);
1202 /* No way to know if this is feasible */
1208 #endif /* COMPRESS */
1213 const char *filename;
1216 #if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__)
1217 # pragma unused(filename)
1220 docompress_file(filename, FALSE);
1225 /* uncompress file if it exists */
1227 uncompress(filename)
1228 const char *filename;
1231 #if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__)
1232 # pragma unused(filename)
1235 docompress_file(filename, TRUE);
1239 /* ---------- END FILE COMPRESSION HANDLING ----------- */
1242 /* ---------- BEGIN FILE LOCKING HANDLING ----------- */
1244 static int nesting = 0;
1246 #ifdef NO_FILE_LINKS /* implies UNIX */
1247 static int lockfd; /* for lock_file() to pass to unlock_file() */
1250 #define HUP if (!program_state.done_hup)
1253 make_lockname(filename, lockname)
1254 const char *filename;
1257 #if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__)
1258 # pragma unused(filename,lockname)
1261 # if defined(UNIX) || defined(VMS) || defined(AMIGA) || defined(WIN32) || defined(MSDOS)
1262 # ifdef NO_FILE_LINKS
1263 Strcpy(lockname, LOCKDIR);
1264 Strcat(lockname, "/");
1265 Strcat(lockname, filename);
1267 Strcpy(lockname, filename);
1271 char *semi_colon = rindex(lockname, ';');
1272 if (semi_colon) *semi_colon = '\0';
1274 Strcat(lockname, ".lock;1");
1276 Strcat(lockname, "_lock");
1282 # endif /* UNIX || VMS || AMIGA || WIN32 || MSDOS */
1289 lock_file(filename, whichprefix, retryct)
1290 const char *filename;
1294 #if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__)
1295 # pragma unused(filename, retryct)
1297 char locknambuf[BUFSZ];
1298 const char *lockname;
1302 impossible("TRIED TO NEST LOCKS");
1306 lockname = make_lockname(filename, locknambuf);
1307 filename = fqname(filename, whichprefix, 0);
1308 #ifndef NO_FILE_LINKS /* LOCKDIR should be subsumed by LOCKPREFIX */
1309 lockname = fqname(lockname, LOCKPREFIX, 2);
1312 #if defined(UNIX) || defined(VMS)
1313 # ifdef NO_FILE_LINKS
1314 while ((lockfd = open(lockname, O_RDWR|O_CREAT|O_EXCL, 0666)) == -1) {
1316 while (link(filename, lockname) == -1) {
1318 register int errnosv = errno;
1320 switch (errnosv) { /* George Barbanis */
1324 "Waiting for access to %s. (%d retries left).",
1326 # if defined(SYSV) || defined(ULTRIX) || defined(VMS)
1331 HUP (void) raw_print("I give up. Sorry.");
1332 HUP raw_printf("Perhaps there is an old %s around?",
1340 HUP raw_printf("Can't find file %s to lock!", filename);
1344 HUP raw_printf("No write permission to lock %s!", filename);
1347 # ifdef VMS /* c__translate(vmsfiles.c) */
1349 /* could be misleading, but usually right */
1350 HUP raw_printf("Can't lock %s due to directory protection.",
1356 HUP perror(lockname);
1358 "Cannot lock %s for unknown reason (%d).",
1365 #endif /* UNIX || VMS */
1367 #if defined(AMIGA) || defined(WIN32) || defined(MSDOS)
1369 #define OPENFAILURE(fd) (!fd)
1372 #define OPENFAILURE(fd) (fd < 0)
1375 while (--retryct && OPENFAILURE(lockptr)) {
1376 # if defined(WIN32) && !defined(WIN_CE)
1377 lockptr = sopen(lockname, O_RDWR|O_CREAT, SH_DENYRW, S_IWRITE);
1379 (void)DeleteFile(lockname); /* in case dead process was here first */
1381 lockptr = Open(lockname,MODE_NEWFILE);
1383 lockptr = open(lockname, O_RDWR|O_CREAT|O_EXCL, S_IWRITE);
1386 if (OPENFAILURE(lockptr)) {
1387 raw_printf("Waiting for access to %s. (%d retries left).",
1393 raw_printf("I give up. Sorry.");
1397 #endif /* AMIGA || WIN32 || MSDOS */
1402 #ifdef VMS /* for unlock_file, use the unlink() routine in vmsunix.c */
1406 # define unlink(foo) vms_unlink(foo)
1409 /* unlock file, which must be currently locked by lock_file */
1411 unlock_file(filename)
1412 const char *filename;
1413 #if defined(macintosh) && (defined(__SC__) || defined(__MRC__))
1414 # pragma unused(filename)
1417 char locknambuf[BUFSZ];
1418 const char *lockname;
1421 lockname = make_lockname(filename, locknambuf);
1422 #ifndef NO_FILE_LINKS /* LOCKDIR should be subsumed by LOCKPREFIX */
1423 lockname = fqname(lockname, LOCKPREFIX, 2);
1426 #if defined(UNIX) || defined(VMS)
1427 if (unlink(lockname) < 0)
1428 HUP raw_printf("Can't unlink %s.", lockname);
1429 # ifdef NO_FILE_LINKS
1430 (void) close(lockfd);
1433 #endif /* UNIX || VMS */
1435 #if defined(AMIGA) || defined(WIN32) || defined(MSDOS)
1436 if (lockptr) Close(lockptr);
1437 DeleteFile(lockname);
1439 #endif /* AMIGA || WIN32 || MSDOS */
1445 /* ---------- END FILE LOCKING HANDLING ----------- */
1448 /* ---------- BEGIN CONFIG FILE HANDLING ----------- */
1450 const char *configfile =
1454 # if defined(MAC) || defined(__BEOS__)
1457 # if defined(MSDOS) || defined(WIN32)
1467 /* conflict with speed-dial under windows
1468 * for XXX.cnf file so support of NetHack.cnf
1469 * is for backward compatibility only.
1470 * Preferred name (and first tried) is now defaults.nh but
1471 * the game will try the old name if there
1472 * is no defaults.nh.
1474 const char *backward_compat_configfile = "nethack.cnf";
1478 #define fopenp fopen
1482 fopen_config_file(filename)
1483 const char *filename;
1486 #if defined(UNIX) || defined(VMS)
1487 char tmp_config[BUFSZ];
1491 /* "filename" is an environment variable, so it should hang around */
1492 /* if set, it is expected to be a full path name (if relevant) */
1495 if (access(filename, 4) == -1) {
1496 /* 4 is R_OK on newer systems */
1497 /* nasty sneaky attempt to read file through
1498 * NetHack's setuid permissions -- this is the only
1499 * place a file name may be wholly under the player's
1502 raw_printf("Access to %s denied (%d).",
1505 /* fall through to standard names */
1508 if ((fp = fopenp(filename, "r")) != (FILE *)0) {
1509 configfile = filename;
1511 #if defined(UNIX) || defined(VMS)
1513 /* access() above probably caught most problems for UNIX */
1514 raw_printf("Couldn't open requested config file %s (%d).",
1517 /* fall through to standard names */
1522 #if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32)
1523 if ((fp = fopenp(fqname(configfile, CONFIGPREFIX, 0), "r"))
1527 else if ((fp = fopenp(fqname(backward_compat_configfile,
1528 CONFIGPREFIX, 0), "r")) != (FILE *)0)
1532 /* constructed full path names don't need fqname() */
1534 if ((fp = fopenp(fqname("nethackini", CONFIGPREFIX, 0), "r"))
1536 configfile = "nethackini";
1539 if ((fp = fopenp("sys$login:nethack.ini", "r")) != (FILE *)0) {
1540 configfile = "nethack.ini";
1544 envp = nh_getenv("HOME");
1546 Strcpy(tmp_config, "NetHack.cnf");
1548 Sprintf(tmp_config, "%s%s", envp, "NetHack.cnf");
1549 if ((fp = fopenp(tmp_config, "r")) != (FILE *)0)
1551 # else /* should be only UNIX left */
1552 envp = nh_getenv("HOME");
1554 Strcpy(tmp_config, configfile);
1556 Sprintf(tmp_config, "%s/%s", envp, configfile);
1557 if ((fp = fopenp(tmp_config, "r")) != (FILE *)0)
1559 # if defined(__APPLE__)
1560 /* try an alternative */
1562 Sprintf(tmp_config, "%s/%s", envp, "Library/Preferences/NetHack Defaults");
1563 if ((fp = fopenp(tmp_config, "r")) != (FILE *)0)
1565 Sprintf(tmp_config, "%s/%s", envp, "Library/Preferences/NetHack Defaults.txt");
1566 if ((fp = fopenp(tmp_config, "r")) != (FILE *)0)
1570 if (errno != ENOENT) {
1573 /* e.g., problems when setuid NetHack can't search home
1574 * directory restricted to user */
1576 #if defined (NHSTDC) && !defined(NOTSTDC)
1577 if ((details = strerror(errno)) == 0)
1580 raw_printf("Couldn't open default config file %s %s(%d).",
1581 tmp_config, details, errno);
1592 * Retrieve a list of integers from a file into a uchar array.
1594 * NOTE: zeros are inserted unless modlist is TRUE, in which case the list
1595 * location is unchanged. Callers must handle zeros if modlist is FALSE.
1598 get_uchars(fp, buf, bufp, list, modlist, size, name)
1599 FILE *fp; /* input file pointer */
1600 char *buf; /* read buffer, must be of size BUFSZ */
1601 char *bufp; /* current pointer */
1602 uchar *list; /* return list */
1603 boolean modlist; /* TRUE: list is being modified in place */
1604 int size; /* return list size */
1605 const char *name; /* name of option for error message */
1607 unsigned int num = 0;
1609 boolean havenum = FALSE;
1613 case ' ': case '\0':
1614 case '\t': case '\n':
1616 /* if modifying in place, don't insert zeros */
1617 if (num || !modlist) list[count] = num;
1622 if (count == size || !*bufp) return count;
1626 case '0': case '1': case '2': case '3':
1627 case '4': case '5': case '6': case '7':
1630 num = num*10 + (*bufp-'0');
1635 if (fp == (FILE *)0)
1638 if (!fgets(buf, BUFSZ, fp)) goto gi_error;
1639 } while (buf[0] == '#');
1645 raw_printf("Syntax error in %s", name);
1653 #ifdef NOCWD_ASSUMPTIONS
1655 adjust_prefix(bufp, prefixid)
1662 /* Backward compatibility, ignore trailing ;n */
1663 if ((ptr = index(bufp, ';')) != 0) *ptr = '\0';
1664 if (strlen(bufp) > 0) {
1665 fqn_prefix[prefixid] = (char *)alloc(strlen(bufp)+2);
1666 Strcpy(fqn_prefix[prefixid], bufp);
1667 append_slash(fqn_prefix[prefixid]);
1672 #define match_varname(INP,NAM,LEN) match_optname(INP, NAM, LEN, TRUE)
1676 parse_config_line(fp, buf, tmp_ramdisk, tmp_levels)
1682 #if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__)
1683 # pragma unused(tmp_ramdisk,tmp_levels)
1686 uchar translate[MAXPCHARS];
1692 /* remove trailing whitespace */
1694 while (--bufp > buf && isspace(*bufp))
1698 return 1; /* skip all-blank lines */
1700 *(bufp + 1) = '\0'; /* terminate line */
1702 /* find the '=' or ':' */
1703 bufp = index(buf, '=');
1704 altp = index(buf, ':');
1705 if (!bufp || (altp && altp < bufp)) bufp = altp;
1706 if (!bufp) return 0;
1708 /* skip whitespace between '=' and value */
1709 do { ++bufp; } while (isspace(*bufp));
1711 /* Go through possible variables */
1712 /* some of these (at least LEVELS and SAVE) should now set the
1713 * appropriate fqn_prefix[] rather than specialized variables
1715 if (match_varname(buf, "OPTIONS", 4)) {
1716 parseoptions(bufp, TRUE, TRUE);
1717 if (plname[0]) /* If a name was given */
1718 plnamesuffix(); /* set the character class */
1719 #ifdef AUTOPICKUP_EXCEPTIONS
1720 } else if (match_varname(buf, "AUTOPICKUP_EXCEPTION", 5)) {
1721 add_autopickup_exception(bufp);
1723 #ifdef NOCWD_ASSUMPTIONS
1724 } else if (match_varname(buf, "HACKDIR", 4)) {
1725 adjust_prefix(bufp, HACKPREFIX);
1726 } else if (match_varname(buf, "LEVELDIR", 4) ||
1727 match_varname(buf, "LEVELS", 4)) {
1728 adjust_prefix(bufp, LEVELPREFIX);
1729 } else if (match_varname(buf, "SAVEDIR", 4)) {
1730 adjust_prefix(bufp, SAVEPREFIX);
1731 } else if (match_varname(buf, "BONESDIR", 5)) {
1732 adjust_prefix(bufp, BONESPREFIX);
1733 } else if (match_varname(buf, "DATADIR", 4)) {
1734 adjust_prefix(bufp, DATAPREFIX);
1735 } else if (match_varname(buf, "SCOREDIR", 4)) {
1736 adjust_prefix(bufp, SCOREPREFIX);
1737 } else if (match_varname(buf, "LOCKDIR", 4)) {
1738 adjust_prefix(bufp, LOCKPREFIX);
1739 } else if (match_varname(buf, "CONFIGDIR", 4)) {
1740 adjust_prefix(bufp, CONFIGPREFIX);
1741 } else if (match_varname(buf, "TROUBLEDIR", 4)) {
1742 adjust_prefix(bufp, TROUBLEPREFIX);
1743 #else /*NOCWD_ASSUMPTIONS*/
1745 } else if (match_varname(buf, "HACKDIR", 4)) {
1746 (void) strncpy(hackdir, bufp, PATHLEN-1);
1748 } else if (match_varname(buf, "RAMDISK", 3)) {
1749 /* The following ifdef is NOT in the wrong
1750 * place. For now, we accept and silently
1753 (void) strncpy(tmp_ramdisk, bufp, PATHLEN-1);
1756 } else if (match_varname(buf, "LEVELS", 4)) {
1757 (void) strncpy(tmp_levels, bufp, PATHLEN-1);
1759 } else if (match_varname(buf, "SAVE", 4)) {
1761 extern int saveprompt;
1764 if ((ptr = index(bufp, ';')) != 0) {
1767 if (*(ptr+1) == 'n' || *(ptr+1) == 'N') {
1774 saveprompt = flags.asksavedisk;
1777 (void) strncpy(SAVEP, bufp, SAVESIZE-1);
1778 append_slash(SAVEP);
1780 #endif /*NOCWD_ASSUMPTIONS*/
1782 } else if (match_varname(buf, "NAME", 4)) {
1783 (void) strncpy(plname, bufp, PL_NSIZ-1);
1785 } else if (match_varname(buf, "ROLE", 4) ||
1786 match_varname(buf, "CHARACTER", 4)) {
1787 if ((len = str2role(bufp)) >= 0)
1788 flags.initrole = len;
1789 } else if (match_varname(buf, "DOGNAME", 3)) {
1790 (void) strncpy(dogname, bufp, PL_PSIZ-1);
1791 } else if (match_varname(buf, "CATNAME", 3)) {
1792 (void) strncpy(catname, bufp, PL_PSIZ-1);
1794 } else if (match_varname(buf, "BOULDER", 3)) {
1795 (void) get_uchars(fp, buf, bufp, &iflags.bouldersym, TRUE,
1797 } else if (match_varname(buf, "GRAPHICS", 4)) {
1798 len = get_uchars(fp, buf, bufp, translate, FALSE,
1799 MAXPCHARS, "GRAPHICS");
1800 assign_graphics(translate, len, MAXPCHARS, 0);
1801 } else if (match_varname(buf, "DUNGEON", 4)) {
1802 len = get_uchars(fp, buf, bufp, translate, FALSE,
1803 MAXDCHARS, "DUNGEON");
1804 assign_graphics(translate, len, MAXDCHARS, 0);
1805 } else if (match_varname(buf, "TRAPS", 4)) {
1806 len = get_uchars(fp, buf, bufp, translate, FALSE,
1807 MAXTCHARS, "TRAPS");
1808 assign_graphics(translate, len, MAXTCHARS, MAXDCHARS);
1809 } else if (match_varname(buf, "EFFECTS", 4)) {
1810 len = get_uchars(fp, buf, bufp, translate, FALSE,
1811 MAXECHARS, "EFFECTS");
1812 assign_graphics(translate, len, MAXECHARS, MAXDCHARS+MAXTCHARS);
1814 } else if (match_varname(buf, "OBJECTS", 3)) {
1815 /* oc_syms[0] is the RANDOM object, unused */
1816 (void) get_uchars(fp, buf, bufp, &(oc_syms[1]), TRUE,
1817 MAXOCLASSES-1, "OBJECTS");
1818 } else if (match_varname(buf, "MONSTERS", 3)) {
1819 /* monsyms[0] is unused */
1820 (void) get_uchars(fp, buf, bufp, &(monsyms[1]), TRUE,
1821 MAXMCLASSES-1, "MONSTERS");
1822 } else if (match_varname(buf, "WARNINGS", 5)) {
1823 (void) get_uchars(fp, buf, bufp, translate, FALSE,
1824 WARNCOUNT, "WARNINGS");
1825 assign_warnings(translate);
1827 } else if (match_varname(buf, "WIZKIT", 6)) {
1828 (void) strncpy(wizkit, bufp, WIZKIT_MAX-1);
1831 } else if (match_varname(buf, "FONT", 4)) {
1834 if( t = strchr( buf+5, ':' ) )
1837 amii_set_text_font( buf+5, atoi( t + 1 ) );
1840 } else if (match_varname(buf, "PATH", 4)) {
1841 (void) strncpy(PATH, bufp, PATHLEN-1);
1842 } else if (match_varname(buf, "DEPTH", 5)) {
1843 extern int amii_numcolors;
1844 int val = atoi( bufp );
1845 amii_numcolors = 1L << min( DEPTH, val );
1846 } else if (match_varname(buf, "DRIPENS", 7)) {
1849 for (i = 0, t = strtok(bufp, ",/"); t != (char *)0;
1850 i < 20 && (t = strtok((char*)0, ",/")), ++i) {
1851 sscanf(t, "%d", &val );
1852 flags.amii_dripens[i] = val;
1854 } else if (match_varname(buf, "SCREENMODE", 10 )) {
1855 extern long amii_scrnmode;
1856 if (!stricmp(bufp,"req"))
1857 amii_scrnmode = 0xffffffff; /* Requester */
1858 else if( sscanf(bufp, "%x", &amii_scrnmode) != 1 )
1860 } else if (match_varname(buf, "MSGPENS", 7)) {
1861 extern int amii_msgAPen, amii_msgBPen;
1862 char *t = strtok(bufp, ",/");
1865 sscanf(t, "%d", &amii_msgAPen);
1866 if( t = strtok((char*)0, ",/") )
1867 sscanf(t, "%d", &amii_msgBPen);
1869 } else if (match_varname(buf, "TEXTPENS", 8)) {
1870 extern int amii_textAPen, amii_textBPen;
1871 char *t = strtok(bufp, ",/");
1874 sscanf(t, "%d", &amii_textAPen);
1875 if( t = strtok((char*)0, ",/") )
1876 sscanf(t, "%d", &amii_textBPen);
1878 } else if (match_varname(buf, "MENUPENS", 8)) {
1879 extern int amii_menuAPen, amii_menuBPen;
1880 char *t = strtok(bufp, ",/");
1883 sscanf(t, "%d", &amii_menuAPen);
1884 if( t = strtok((char*)0, ",/") )
1885 sscanf(t, "%d", &amii_menuBPen);
1887 } else if (match_varname(buf, "STATUSPENS", 10)) {
1888 extern int amii_statAPen, amii_statBPen;
1889 char *t = strtok(bufp, ",/");
1892 sscanf(t, "%d", &amii_statAPen);
1893 if( t = strtok((char*)0, ",/") )
1894 sscanf(t, "%d", &amii_statBPen);
1896 } else if (match_varname(buf, "OTHERPENS", 9)) {
1897 extern int amii_otherAPen, amii_otherBPen;
1898 char *t = strtok(bufp, ",/");
1901 sscanf(t, "%d", &amii_otherAPen);
1902 if( t = strtok((char*)0, ",/") )
1903 sscanf(t, "%d", &amii_otherBPen);
1905 } else if (match_varname(buf, "PENS", 4)) {
1906 extern unsigned short amii_init_map[ AMII_MAXCOLORS ];
1910 for (i = 0, t = strtok(bufp, ",/");
1911 i < AMII_MAXCOLORS && t != (char *)0;
1912 t = strtok((char *)0, ",/"), ++i)
1914 sscanf(t, "%hx", &amii_init_map[i]);
1916 amii_setpens( amii_numcolors = i );
1917 } else if (match_varname(buf, "FGPENS", 6)) {
1918 extern int foreg[ AMII_MAXCOLORS ];
1922 for (i = 0, t = strtok(bufp, ",/");
1923 i < AMII_MAXCOLORS && t != (char *)0;
1924 t = strtok((char *)0, ",/"), ++i)
1926 sscanf(t, "%d", &foreg[i]);
1928 } else if (match_varname(buf, "BGPENS", 6)) {
1929 extern int backg[ AMII_MAXCOLORS ];
1933 for (i = 0, t = strtok(bufp, ",/");
1934 i < AMII_MAXCOLORS && t != (char *)0;
1935 t = strtok((char *)0, ",/"), ++i)
1937 sscanf(t, "%d", &backg[i]);
1941 } else if (match_varname(buf, "SOUNDDIR", 8)) {
1942 sounddir = (char *)strdup(bufp);
1943 } else if (match_varname(buf, "SOUND", 5)) {
1944 add_sound_mapping(bufp);
1947 /* These should move to wc_ options */
1948 } else if (match_varname(buf, "QT_TILEWIDTH", 12)) {
1949 extern char *qt_tilewidth;
1950 if (qt_tilewidth == NULL)
1951 qt_tilewidth=(char *)strdup(bufp);
1952 } else if (match_varname(buf, "QT_TILEHEIGHT", 13)) {
1953 extern char *qt_tileheight;
1954 if (qt_tileheight == NULL)
1955 qt_tileheight=(char *)strdup(bufp);
1956 } else if (match_varname(buf, "QT_FONTSIZE", 11)) {
1957 extern char *qt_fontsize;
1958 if (qt_fontsize == NULL)
1959 qt_fontsize=(char *)strdup(bufp);
1960 } else if (match_varname(buf, "QT_COMPACT", 10)) {
1961 extern int qt_compact_mode;
1962 qt_compact_mode = atoi(bufp);
1971 can_read_file(filename)
1972 const char *filename;
1974 return (access(filename, 4) == 0);
1976 #endif /* USER_SOUNDS */
1979 read_config_file(filename)
1980 const char *filename;
1982 #define tmp_levels (char *)0
1983 #define tmp_ramdisk (char *)0
1985 #if defined(MICRO) || defined(WIN32)
1987 char tmp_levels[PATHLEN];
1991 char tmp_ramdisk[PATHLEN];
1998 if (!(fp = fopen_config_file(filename))) return;
2000 #if defined(MICRO) || defined(WIN32)
2008 /* begin detection of duplicate configfile options */
2009 set_duplicate_opt_detection(1);
2011 while (fgets(buf, 4*BUFSZ, fp)) {
2012 if (!parse_config_line(fp, buf, tmp_ramdisk, tmp_levels)) {
2013 raw_printf("Bad option line: \"%.50s\"", buf);
2019 /* turn off detection of duplicate configfile options */
2020 set_duplicate_opt_detection(0);
2022 #if defined(MICRO) && !defined(NOCWD_ASSUMPTIONS)
2023 /* should be superseded by fqn_prefix[] */
2025 Strcpy(permbones, tmp_levels);
2027 if (tmp_ramdisk[0]) {
2028 Strcpy(levels, tmp_ramdisk);
2029 if (strcmp(permbones, levels)) /* if not identical */
2033 Strcpy(levels, tmp_levels);
2035 Strcpy(bones, levels);
2036 # endif /* MFLOPPY */
2046 #if defined(VMS) || defined(UNIX)
2047 char tmp_wizkit[BUFSZ];
2051 envp = nh_getenv("WIZKIT");
2052 if (envp && *envp) (void) strncpy(wizkit, envp, WIZKIT_MAX - 1);
2053 if (!wizkit[0]) return (FILE *)0;
2056 if (access(wizkit, 4) == -1) {
2057 /* 4 is R_OK on newer systems */
2058 /* nasty sneaky attempt to read file through
2059 * NetHack's setuid permissions -- this is a
2060 * place a file name may be wholly under the player's
2063 raw_printf("Access to %s denied (%d).",
2066 /* fall through to standard names */
2069 if ((fp = fopenp(wizkit, "r")) != (FILE *)0) {
2071 #if defined(UNIX) || defined(VMS)
2073 /* access() above probably caught most problems for UNIX */
2074 raw_printf("Couldn't open requested config file %s (%d).",
2080 #if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32)
2081 if ((fp = fopenp(fqname(wizkit, CONFIGPREFIX, 0), "r"))
2086 envp = nh_getenv("HOME");
2088 Sprintf(tmp_wizkit, "%s%s", envp, wizkit);
2090 Sprintf(tmp_wizkit, "%s%s", "sys$login:", wizkit);
2091 if ((fp = fopenp(tmp_wizkit, "r")) != (FILE *)0)
2093 # else /* should be only UNIX left */
2094 envp = nh_getenv("HOME");
2096 Sprintf(tmp_wizkit, "%s/%s", envp, wizkit);
2097 else Strcpy(tmp_wizkit, wizkit);
2098 if ((fp = fopenp(tmp_wizkit, "r")) != (FILE *)0)
2100 else if (errno != ENOENT) {
2101 /* e.g., problems when setuid NetHack can't search home
2102 * directory restricted to user */
2103 raw_printf("Couldn't open default wizkit file %s (%d).",
2116 char *ep, buf[BUFSZ];
2118 boolean bad_items = FALSE, skip = FALSE;
2120 if (!wizard || !(fp = fopen_wizkit_file())) return;
2122 while (fgets(buf, (int)(sizeof buf), fp)) {
2123 ep = index(buf, '\n');
2124 if (skip) { /* in case previous line was too long */
2125 if (ep) skip = FALSE; /* found newline; next line is normal */
2127 if (!ep) skip = TRUE; /* newline missing; discard next fgets */
2128 else *ep = '\0'; /* remove newline */
2131 otmp = readobjnam(buf, (struct obj *)0, FALSE);
2133 if (otmp != &zeroobj)
2134 otmp = addinv(otmp);
2136 /* .60 limits output line width to 79 chars */
2137 raw_printf("Bad wizkit item: \"%.60s\"", buf);
2151 /* ---------- END CONFIG FILE HANDLING ----------- */
2153 /* ---------- BEGIN SCOREBOARD CREATION ----------- */
2155 /* verify that we can write to the scoreboard file; if not, try to create one */
2157 check_recordfile(dir)
2160 #if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__)
2161 # pragma unused(dir)
2163 const char *fq_record;
2166 #if defined(UNIX) || defined(VMS)
2167 fq_record = fqname(RECORD, SCOREPREFIX, 0);
2168 fd = open(fq_record, O_RDWR, 0);
2170 # ifdef VMS /* must be stream-lf to use UPDATE_RECORD_IN_PLACE */
2171 if (!file_is_stmlf(fd)) {
2173 "Warning: scoreboard file %s is not in stream_lf format",
2178 (void) close(fd); /* RECORD is accessible */
2179 } else if ((fd = open(fq_record, O_CREAT|O_RDWR, FCMASK)) >= 0) {
2180 (void) close(fd); /* RECORD newly created */
2181 # if defined(VMS) && !defined(SECURE)
2182 /* Re-protect RECORD with world:read+write+execute+delete access. */
2183 (void) chmod(fq_record, FCMASK | 007);
2184 # endif /* VMS && !SECURE */
2186 raw_printf("Warning: cannot write scoreboard file %s", fq_record);
2189 #endif /* !UNIX && !VMS */
2190 #if defined(MICRO) || defined(WIN32)
2193 # ifdef OS2_CODEVIEW /* explicit path on opening for OS/2 */
2194 /* how does this work when there isn't an explicit path or fopenp
2195 * for later access to the file via fopen_datafile? ? */
2196 (void) strncpy(tmp, dir, PATHLEN - 1);
2197 tmp[PATHLEN-1] = '\0';
2198 if ((strlen(tmp) + 1 + strlen(RECORD)) < (PATHLEN - 1)) {
2200 Strcat(tmp, RECORD);
2204 Strcpy(tmp, RECORD);
2205 fq_record = fqname(RECORD, SCOREPREFIX, 0);
2208 if ((fd = open(fq_record, O_RDWR)) < 0) {
2209 /* try to create empty record */
2210 # if defined(AZTEC_C) || defined(_DCC) || (defined(__GNUC__) && defined(__AMIGA__))
2211 /* Aztec doesn't use the third argument */
2212 /* DICE doesn't like it */
2213 if ((fd = open(fq_record, O_CREAT|O_RDWR)) < 0) {
2215 if ((fd = open(fq_record, O_CREAT|O_RDWR, S_IREAD|S_IWRITE)) < 0) {
2217 raw_printf("Warning: cannot write record %s", tmp);
2221 } else /* open succeeded */
2223 #else /* MICRO || WIN32*/
2226 /* Create the "record" file, if necessary */
2227 fq_record = fqname(RECORD, SCOREPREFIX, 0);
2228 fd = macopen (fq_record, O_RDWR | O_CREAT, TEXT_TYPE);
2229 if (fd != -1) macclose (fd);
2232 #endif /* MICRO || WIN32*/
2235 /* ---------- END SCOREBOARD CREATION ----------- */
2237 /* ---------- BEGIN PANIC/IMPOSSIBLE LOG ----------- */
2241 paniclog(type, reason)
2242 const char *type; /* panic, impossible, trickery */
2243 const char *reason; /* explanation */
2249 if (!program_state.in_paniclog) {
2250 program_state.in_paniclog = 1;
2251 lfile = fopen_datafile(PANICLOG, "a", TROUBLEPREFIX);
2253 (void) fprintf(lfile, "%s %08ld: %s %s\n",
2254 version_string(buf), yyyymmdd((time_t)0L),
2256 (void) fclose(lfile);
2258 program_state.in_paniclog = 0;
2260 #endif /* PANICLOG */
2264 /* ---------- END PANIC/IMPOSSIBLE LOG ----------- */
2268 /* ---------- BEGIN INTERNAL RECOVER ----------- */
2273 int lev, savelev, hpid;
2275 struct version_info version_data;
2277 char savename[SAVESIZE], errbuf[BUFSZ];
2279 for (lev = 0; lev < 256; lev++)
2282 /* level 0 file contains:
2283 * pid of creating process (ignored here)
2284 * level number for current level of save file
2285 * name of save file nethack would have created
2288 gfd = open_levelfile(0, errbuf);
2290 raw_printf("%s\n", errbuf);
2293 if (read(gfd, (genericptr_t) &hpid, sizeof hpid) != sizeof hpid) {
2295 "\nCheckpoint data incompletely written or subsequently clobbered. Recovery impossible.");
2299 if (read(gfd, (genericptr_t) &savelev, sizeof(savelev))
2300 != sizeof(savelev)) {
2301 raw_printf("\nCheckpointing was not in effect for %s -- recovery impossible.\n",
2306 if ((read(gfd, (genericptr_t) savename, sizeof savename)
2307 != sizeof savename) ||
2308 (read(gfd, (genericptr_t) &version_data, sizeof version_data)
2309 != sizeof version_data)) {
2310 raw_printf("\nError reading %s -- can't recover.\n", lock);
2315 /* save file should contain:
2317 * current level (including pets)
2318 * (non-level-based) game state
2321 set_savefile_name();
2322 sfd = create_savefile();
2324 raw_printf("\nCannot recover savefile %s.\n", SAVEF);
2329 lfd = open_levelfile(savelev, errbuf);
2331 raw_printf("\n%s\n", errbuf);
2338 if (write(sfd, (genericptr_t) &version_data, sizeof version_data)
2339 != sizeof version_data) {
2340 raw_printf("\nError writing %s; recovery failed.", SAVEF);
2347 if (!copy_bytes(lfd, sfd)) {
2354 processed[savelev] = 1;
2356 if (!copy_bytes(gfd, sfd)) {
2365 for (lev = 1; lev < 256; lev++) {
2366 /* level numbers are kept in xchars in save.c, so the
2367 * maximum level number (for the endlevel) must be < 256
2369 if (lev != savelev) {
2370 lfd = open_levelfile(lev, (char *)0);
2372 /* any or all of these may not exist */
2374 write(sfd, (genericptr_t) &levc, sizeof(levc));
2375 if (!copy_bytes(lfd, sfd)) {
2388 #ifdef HOLD_LOCKFILE_OPEN
2392 * We have a successful savefile!
2393 * Only now do we erase the level files.
2395 for (lev = 0; lev < 256; lev++) {
2396 if (processed[lev]) {
2397 const char *fq_lock;
2398 set_levelfile_name(lock, lev);
2399 fq_lock = fqname(lock, LEVELPREFIX, 3);
2400 (void) unlink(fq_lock);
2407 copy_bytes(ifd, ofd)
2414 nfrom = read(ifd, buf, BUFSIZ);
2415 nto = write(ofd, buf, nfrom);
2416 if (nto != nfrom) return FALSE;
2417 } while (nfrom == BUFSIZ);
2421 /* ---------- END INTERNAL RECOVER ----------- */
2422 #endif /*SELF_RECOVER*/