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. */ 4 5#include "hack.h" 6#include "dlb.h" 7 8#ifdef TTY_GRAPHICS 9#include "wintty.h" /* more() */ 10#endif 11 12#include <ctype.h> 13 14#if !defined(MAC) && !defined(O_WRONLY) && !defined(AZTEC_C) 15#include <fcntl.h> 16#endif 17 18#include <errno.h> 19#ifdef _MSC_VER /* MSC 6.0 defines errno quite differently */ 20# if (_MSC_VER >= 600) 21# define SKIP_ERRNO 22# endif 23#else 24# ifdef NHSTDC 25# define SKIP_ERRNO 26# endif 27#endif 28#ifndef SKIP_ERRNO 29# ifdef _DCC 30const 31# endif 32extern int errno; 33#endif 34 35#if defined(UNIX) && defined(QT_GRAPHICS) 36#include <dirent.h> 37#endif 38 39#if defined(UNIX) || defined(VMS) 40#include <signal.h> 41#endif 42 43#if defined(MSDOS) || defined(OS2) || defined(TOS) || defined(WIN32) 44# ifndef GNUDOS 45#include <sys\stat.h> 46# else 47#include <sys/stat.h> 48# endif 49#endif 50#ifndef O_BINARY /* used for micros, no-op for others */ 51# define O_BINARY 0 52#endif 53 54#ifdef PREFIXES_IN_USE 55#define FQN_NUMBUF 4 56static char fqn_filename_buffer[FQN_NUMBUF][FQN_MAX_FILENAME]; 57#endif 58 59#if !defined(MFLOPPY) && !defined(VMS) && !defined(WIN32) 60char bones[] = "bonesnn.xxx"; 61char lock[PL_NSIZ+14] = "1lock"; /* long enough for uid+name+.99 */ 62#else 63# if defined(MFLOPPY) 64char bones[FILENAME]; /* pathname of bones files */ 65char lock[FILENAME]; /* pathname of level files */ 66# endif 67# if defined(VMS) 68char bones[] = "bonesnn.xxx;1"; 69char lock[PL_NSIZ+17] = "1lock"; /* long enough for _uid+name+.99;1 */ 70# endif 71# if defined(WIN32) 72char bones[] = "bonesnn.xxx"; 73char lock[PL_NSIZ+25]; /* long enough for username+-+name+.99 */ 74# endif 75#endif 76 77#if defined(UNIX) || defined(__BEOS__) 78#define SAVESIZE (PL_NSIZ + 13) /* save/99999player.e */ 79#else 80# ifdef VMS 81#define SAVESIZE (PL_NSIZ + 22) /* [.save]<uid>player.e;1 */ 82# else 83# if defined(WIN32) 84#define SAVESIZE (PL_NSIZ + 40) /* username-player.NetHack-saved-game */ 85# else 86#define SAVESIZE FILENAME /* from macconf.h or pcconf.h */ 87# endif 88# endif 89#endif 90 91char SAVEF[SAVESIZE]; /* holds relative path of save file from playground */ 92#ifdef MICRO 93char SAVEP[SAVESIZE]; /* holds path of directory for save file */ 94#endif 95 96#ifdef HOLD_LOCKFILE_OPEN 97struct level_ftrack { 98int init; 99int fd; /* file descriptor for level file */ 100int oflag; /* open flags */ 101boolean nethack_thinks_it_is_open; /* Does NetHack think it's open? */ 102} lftrack; 103# if defined(WIN32) 104#include <share.h> 105# endif 106#endif /*HOLD_LOCKFILE_OPEN*/ 107 108#ifdef WIZARD 109#define WIZKIT_MAX 128 110static char wizkit[WIZKIT_MAX]; 111STATIC_DCL FILE *NDECL(fopen_wizkit_file); 112#endif 113 114#ifdef AMIGA 115extern char PATH[]; /* see sys/amiga/amidos.c */ 116extern char bbs_id[]; 117static int lockptr; 118# ifdef __SASC_60 119#include <proto/dos.h> 120# endif 121 122#include <libraries/dos.h> 123extern void FDECL(amii_set_text_font, ( char *, int )); 124#endif 125 126#if defined(WIN32) || defined(MSDOS) 127static int lockptr; 128# ifdef MSDOS 129#define Delay(a) msleep(a) 130# endif 131#define Close close 132#ifndef WIN_CE 133#define DeleteFile unlink 134#endif 135#endif 136 137#ifdef MAC 138# define unlink macunlink 139#endif 140 141// RefOS has no unlink. 142#define unlink(...) 0 143 144 145#ifdef USER_SOUNDS 146extern char *sounddir; 147#endif 148 149extern int n_dgns; /* from dungeon.c */ 150 151STATIC_DCL char *FDECL(set_bonesfile_name, (char *,d_level*)); 152STATIC_DCL char *NDECL(set_bonestemp_name); 153#ifdef COMPRESS 154STATIC_DCL void FDECL(redirect, (const char *,const char *,FILE *,BOOLEAN_P)); 155STATIC_DCL void FDECL(docompress_file, (const char *,BOOLEAN_P)); 156#endif 157STATIC_DCL char *FDECL(make_lockname, (const char *,char *)); 158STATIC_DCL FILE *FDECL(fopen_config_file, (const char *)); 159STATIC_DCL int FDECL(get_uchars, (FILE *,char *,char *,uchar *,BOOLEAN_P,int,const char *)); 160int FDECL(parse_config_line, (FILE *,char *,char *,char *)); 161#ifdef NOCWD_ASSUMPTIONS 162STATIC_DCL void FDECL(adjust_prefix, (char *, int)); 163#endif 164#ifdef SELF_RECOVER 165STATIC_DCL boolean FDECL(copy_bytes, (int, int)); 166#endif 167#ifdef HOLD_LOCKFILE_OPEN 168STATIC_DCL int FDECL(open_levelfile_exclusively, (const char *, int, int)); 169#endif 170 171/* 172 * fname_encode() 173 * 174 * Args: 175 * legal zero-terminated list of acceptable file name characters 176 * quotechar lead-in character used to quote illegal characters as hex digits 177 * s string to encode 178 * callerbuf buffer to house result 179 * bufsz size of callerbuf 180 * 181 * Notes: 182 * The hex digits 0-9 and A-F are always part of the legal set due to 183 * their use in the encoding scheme, even if not explicitly included in 'legal'. 184 * 185 * Sample: 186 * The following call: 187 * (void)fname_encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 188 * '%', "This is a % test!", buf, 512); 189 * results in this encoding: 190 * "This%20is%20a%20%25%20test%21" 191 */ 192char * 193fname_encode(legal, quotechar, s, callerbuf, bufsz) 194const char *legal; 195char quotechar; 196char *s, *callerbuf; 197int bufsz; 198{ 199 char *sp, *op; 200 int cnt = 0; 201 static char hexdigits[] = "0123456789ABCDEF"; 202 203 sp = s; 204 op = callerbuf; 205 *op = '\0'; 206 207 while (*sp) { 208 /* Do we have room for one more character or encoding? */ 209 if ((bufsz - cnt) <= 4) return callerbuf; 210 211 if (*sp == quotechar) { 212 (void)sprintf(op, "%c%02X", quotechar, *sp); 213 op += 3; 214 cnt += 3; 215 } else if ((index(legal, *sp) != 0) || (index(hexdigits, *sp) != 0)) { 216 *op++ = *sp; 217 *op = '\0'; 218 cnt++; 219 } else { 220 (void)sprintf(op,"%c%02X", quotechar, *sp); 221 op += 3; 222 cnt += 3; 223 } 224 sp++; 225 } 226 return callerbuf; 227} 228 229/* 230 * fname_decode() 231 * 232 * Args: 233 * quotechar lead-in character used to quote illegal characters as hex digits 234 * s string to decode 235 * callerbuf buffer to house result 236 * bufsz size of callerbuf 237 */ 238char * 239fname_decode(quotechar, s, callerbuf, bufsz) 240char quotechar; 241char *s, *callerbuf; 242int bufsz; 243{ 244 char *sp, *op; 245 int k,calc,cnt = 0; 246 static char hexdigits[] = "0123456789ABCDEF"; 247 248 sp = s; 249 op = callerbuf; 250 *op = '\0'; 251 calc = 0; 252 253 while (*sp) { 254 /* Do we have room for one more character? */ 255 if ((bufsz - cnt) <= 2) return callerbuf; 256 if (*sp == quotechar) { 257 sp++; 258 for (k=0; k < 16; ++k) if (*sp == hexdigits[k]) break; 259 if (k >= 16) return callerbuf; /* impossible, so bail */ 260 calc = k << 4; 261 sp++; 262 for (k=0; k < 16; ++k) if (*sp == hexdigits[k]) break; 263 if (k >= 16) return callerbuf; /* impossible, so bail */ 264 calc += k; 265 sp++; 266 *op++ = calc; 267 *op = '\0'; 268 } else { 269 *op++ = *sp++; 270 *op = '\0'; 271 } 272 cnt++; 273 } 274 return callerbuf; 275} 276 277#ifndef PREFIXES_IN_USE 278/*ARGSUSED*/ 279#endif 280const char * 281fqname(basename, whichprefix, buffnum) 282const char *basename; 283int whichprefix, buffnum; 284{ 285#ifndef PREFIXES_IN_USE 286 return basename; 287#else 288 if (!basename || whichprefix < 0 || whichprefix >= PREFIX_COUNT) 289 return basename; 290 if (!fqn_prefix[whichprefix]) 291 return basename; 292 if (buffnum < 0 || buffnum >= FQN_NUMBUF) { 293 impossible("Invalid fqn_filename_buffer specified: %d", 294 buffnum); 295 buffnum = 0; 296 } 297 if (strlen(fqn_prefix[whichprefix]) + strlen(basename) >= 298 FQN_MAX_FILENAME) { 299 impossible("fqname too long: %s + %s", fqn_prefix[whichprefix], 300 basename); 301 return basename; /* XXX */ 302 } 303 Strcpy(fqn_filename_buffer[buffnum], fqn_prefix[whichprefix]); 304 return strcat(fqn_filename_buffer[buffnum], basename); 305#endif 306} 307 308/* reasonbuf must be at least BUFSZ, supplied by caller */ 309/*ARGSUSED*/ 310int 311validate_prefix_locations(reasonbuf) 312char *reasonbuf; 313{ 314#if defined(NOCWD_ASSUMPTIONS) 315 FILE *fp; 316 const char *filename; 317 int prefcnt, failcount = 0; 318 char panicbuf1[BUFSZ], panicbuf2[BUFSZ], *details; 319 320 if (reasonbuf) reasonbuf[0] = '\0'; 321 for (prefcnt = 1; prefcnt < PREFIX_COUNT; prefcnt++) { 322 /* don't test writing to configdir or datadir; they're readonly */ 323 if (prefcnt == CONFIGPREFIX || prefcnt == DATAPREFIX) continue; 324 filename = fqname("validate", prefcnt, 3); 325 if ((fp = fopen(filename, "w"))) { 326 fclose(fp); 327 (void) unlink(filename); 328 } else { 329 if (reasonbuf) { 330 if (failcount) Strcat(reasonbuf,", "); 331 Strcat(reasonbuf, fqn_prefix_names[prefcnt]); 332 } 333 /* the paniclog entry gets the value of errno as well */ 334 Sprintf(panicbuf1,"Invalid %s", fqn_prefix_names[prefcnt]); 335#if defined (NHSTDC) && !defined(NOTSTDC) 336 if (!(details = strerror(errno))) 337#endif 338 details = ""; 339 Sprintf(panicbuf2,"\"%s\", (%d) %s", 340 fqn_prefix[prefcnt], errno, details); 341 paniclog(panicbuf1, panicbuf2); 342 failcount++; 343 } 344 } 345 if (failcount) 346 return 0; 347 else 348#endif 349 return 1; 350} 351 352/* fopen a file, with OS-dependent bells and whistles */ 353/* NOTE: a simpler version of this routine also exists in util/dlb_main.c */ 354FILE * 355fopen_datafile(filename, mode, prefix) 356const char *filename, *mode; 357int prefix; 358{ 359 FILE *fp; 360 361 filename = fqname(filename, prefix, prefix == TROUBLEPREFIX ? 3 : 0); 362#ifdef VMS /* essential to have punctuation, to avoid logical names */ 363 { 364 char tmp[BUFSIZ]; 365 366 if (!index(filename, '.') && !index(filename, ';')) 367 filename = strcat(strcpy(tmp, filename), ";0"); 368 fp = fopen(filename, mode, "mbc=16"); 369 } 370#else 371 fp = fopen(filename, mode); 372#endif 373 return fp; 374} 375 376/* ---------- BEGIN LEVEL FILE HANDLING ----------- */ 377 378#ifdef MFLOPPY 379/* Set names for bones[] and lock[] */ 380void 381set_lock_and_bones() 382{ 383 if (!ramdisk) { 384 Strcpy(levels, permbones); 385 Strcpy(bones, permbones); 386 } 387 append_slash(permbones); 388 append_slash(levels); 389#ifdef AMIGA 390 strncat(levels, bbs_id, PATHLEN); 391#endif 392 append_slash(bones); 393 Strcat(bones, "bonesnn.*"); 394 Strcpy(lock, levels); 395#ifndef AMIGA 396 Strcat(lock, alllevels); 397#endif 398 return; 399} 400#endif /* MFLOPPY */ 401 402 403/* Construct a file name for a level-type file, which is of the form 404 * something.level (with any old level stripped off). 405 * This assumes there is space on the end of 'file' to append 406 * a two digit number. This is true for 'level' 407 * but be careful if you use it for other things -dgk 408 */ 409void 410set_levelfile_name(file, lev) 411char *file; 412int lev; 413{ 414 char *tf; 415 416 tf = rindex(file, '.'); 417 if (!tf) tf = eos(file); 418 Sprintf(tf, ".%d", lev); 419#ifdef VMS 420 Strcat(tf, ";1"); 421#endif 422 return; 423} 424 425int 426create_levelfile(lev, errbuf) 427int lev; 428char errbuf[]; 429{ 430 int fd; 431 const char *fq_lock; 432 433 if (errbuf) *errbuf = '\0'; 434 set_levelfile_name(lock, lev); 435 fq_lock = fqname(lock, LEVELPREFIX, 0); 436 437#if defined(MICRO) || defined(WIN32) 438 /* Use O_TRUNC to force the file to be shortened if it already 439 * exists and is currently longer. 440 */ 441# ifdef HOLD_LOCKFILE_OPEN 442 if (lev == 0) 443 fd = open_levelfile_exclusively(fq_lock, lev, 444 O_WRONLY |O_CREAT | O_TRUNC | O_BINARY); 445 else 446# endif 447 fd = open(fq_lock, O_WRONLY |O_CREAT | O_TRUNC | O_BINARY, FCMASK); 448#else 449# ifdef MAC 450 fd = maccreat(fq_lock, LEVL_TYPE); 451# else 452 fd = creat(fq_lock, FCMASK); 453# endif 454#endif /* MICRO || WIN32 */ 455 456 if (fd >= 0) 457 level_info[lev].flags |= LFILE_EXISTS; 458 else if (errbuf) /* failure explanation */ 459 Sprintf(errbuf, 460 "Cannot create file \"%s\" for level %d (errno %d).", 461 lock, lev, errno); 462 463 return fd; 464} 465 466 467int 468open_levelfile(lev, errbuf) 469int lev; 470char errbuf[]; 471{ 472 int fd; 473 const char *fq_lock; 474 475 if (errbuf) *errbuf = '\0'; 476 set_levelfile_name(lock, lev); 477 fq_lock = fqname(lock, LEVELPREFIX, 0); 478#ifdef MFLOPPY 479 /* If not currently accessible, swap it in. */ 480 if (level_info[lev].where != ACTIVE) 481 swapin_file(lev); 482#endif 483#ifdef MAC 484 fd = macopen(fq_lock, O_RDONLY | O_BINARY, LEVL_TYPE); 485#else 486# ifdef HOLD_LOCKFILE_OPEN 487 if (lev == 0) 488 fd = open_levelfile_exclusively(fq_lock, lev, O_RDONLY | O_BINARY ); 489 else 490# endif 491 fd = open(fq_lock, O_RDONLY | O_BINARY, 0); 492#endif 493 494 /* for failure, return an explanation that our caller can use; 495 settle for `lock' instead of `fq_lock' because the latter 496 might end up being too big for nethack's BUFSZ */ 497 if (fd < 0 && errbuf) 498 Sprintf(errbuf, 499 "Cannot open file \"%s\" for level %d (errno %d).", 500 lock, lev, errno); 501 502 return fd; 503} 504 505 506void 507delete_levelfile(lev) 508int lev; 509{ 510 /* 511 * Level 0 might be created by port specific code that doesn't 512 * call create_levfile(), so always assume that it exists. 513 */ 514 if (lev == 0 || (level_info[lev].flags & LFILE_EXISTS)) { 515 set_levelfile_name(lock, lev); 516#ifdef HOLD_LOCKFILE_OPEN 517 if (lev == 0) really_close(); 518#endif 519 (void) unlink(fqname(lock, LEVELPREFIX, 0)); 520 level_info[lev].flags &= ~LFILE_EXISTS; 521 } 522} 523 524 525void 526clearlocks() 527{ 528#if !defined(PC_LOCKING) && defined(MFLOPPY) && !defined(AMIGA) 529 eraseall(levels, alllevels); 530 if (ramdisk) 531 eraseall(permbones, alllevels); 532#else 533 register int x; 534 535# if defined(UNIX) || defined(VMS) 536 (void) signal(SIGHUP, SIG_IGN); 537# endif 538 /* can't access maxledgerno() before dungeons are created -dlc */ 539 for (x = (n_dgns ? maxledgerno() : 0); x >= 0; x--) 540 delete_levelfile(x); /* not all levels need be present */ 541#endif 542} 543 544#ifdef HOLD_LOCKFILE_OPEN 545STATIC_OVL int 546open_levelfile_exclusively(name, lev, oflag) 547const char *name; 548int lev, oflag; 549{ 550 int reslt, fd; 551 if (!lftrack.init) { 552 lftrack.init = 1; 553 lftrack.fd = -1; 554 } 555 if (lftrack.fd >= 0) { 556 /* check for compatible access */ 557 if (lftrack.oflag == oflag) { 558 fd = lftrack.fd; 559 reslt = lseek(fd, 0L, SEEK_SET); 560 if (reslt == -1L) 561 panic("open_levelfile_exclusively: lseek failed %d", errno); 562 lftrack.nethack_thinks_it_is_open = TRUE; 563 } else { 564 really_close(); 565 fd = sopen(name, oflag,SH_DENYRW, FCMASK); 566 lftrack.fd = fd; 567 lftrack.oflag = oflag; 568 lftrack.nethack_thinks_it_is_open = TRUE; 569 } 570 } else { 571 fd = sopen(name, oflag,SH_DENYRW, FCMASK); 572 lftrack.fd = fd; 573 lftrack.oflag = oflag; 574 if (fd >= 0) 575 lftrack.nethack_thinks_it_is_open = TRUE; 576 } 577 return fd; 578} 579 580void 581really_close() 582{ 583 int fd = lftrack.fd; 584 lftrack.nethack_thinks_it_is_open = FALSE; 585 lftrack.fd = -1; 586 lftrack.oflag = 0; 587 (void)_close(fd); 588 return; 589} 590 591int 592close(fd) 593int fd; 594{ 595 if (lftrack.fd == fd) { 596 really_close(); /* close it, but reopen it to hold it */ 597 fd = open_levelfile(0, (char *)0); 598 lftrack.nethack_thinks_it_is_open = FALSE; 599 return 0; 600 } 601 return _close(fd); 602} 603#endif 604 605/* ---------- END LEVEL FILE HANDLING ----------- */ 606 607 608/* ---------- BEGIN BONES FILE HANDLING ----------- */ 609 610/* set up "file" to be file name for retrieving bones, and return a 611 * bonesid to be read/written in the bones file. 612 */ 613STATIC_OVL char * 614set_bonesfile_name(file, lev) 615char *file; 616d_level *lev; 617{ 618 s_level *sptr; 619 char *dptr; 620 621 Sprintf(file, "bon%c%s", dungeons[lev->dnum].boneid, 622 In_quest(lev) ? urole.filecode : "0"); 623 dptr = eos(file); 624 if ((sptr = Is_special(lev)) != 0) 625 Sprintf(dptr, ".%c", sptr->boneid); 626 else 627 Sprintf(dptr, ".%d", lev->dlevel); 628#ifdef VMS 629 Strcat(dptr, ";1"); 630#endif 631 return(dptr-2); 632} 633 634/* set up temporary file name for writing bones, to avoid another game's 635 * trying to read from an uncompleted bones file. we want an uncontentious 636 * name, so use one in the namespace reserved for this game's level files. 637 * (we are not reading or writing level files while writing bones files, so 638 * the same array may be used instead of copying.) 639 */ 640STATIC_OVL char * 641set_bonestemp_name() 642{ 643 char *tf; 644 645 tf = rindex(lock, '.'); 646 if (!tf) tf = eos(lock); 647 Sprintf(tf, ".bn"); 648#ifdef VMS 649 Strcat(tf, ";1"); 650#endif 651 return lock; 652} 653 654int 655create_bonesfile(lev, bonesid, errbuf) 656d_level *lev; 657char **bonesid; 658char errbuf[]; 659{ 660 const char *file; 661 int fd; 662 663 if (errbuf) *errbuf = '\0'; 664 *bonesid = set_bonesfile_name(bones, lev); 665 file = set_bonestemp_name(); 666 file = fqname(file, BONESPREFIX, 0); 667 668#if defined(MICRO) || defined(WIN32) 669 /* Use O_TRUNC to force the file to be shortened if it already 670 * exists and is currently longer. 671 */ 672 fd = open(file, O_WRONLY |O_CREAT | O_TRUNC | O_BINARY, FCMASK); 673#else 674# ifdef MAC 675 fd = maccreat(file, BONE_TYPE); 676# else 677 fd = creat(file, FCMASK); 678# endif 679#endif 680 if (fd < 0 && errbuf) /* failure explanation */ 681 Sprintf(errbuf, 682 "Cannot create bones \"%s\", id %s (errno %d).", 683 lock, *bonesid, errno); 684 685# if defined(VMS) && !defined(SECURE) 686 /* 687 Re-protect bones file with world:read+write+execute+delete access. 688 umask() doesn't seem very reliable; also, vaxcrtl won't let us set 689 delete access without write access, which is what's really wanted. 690 Can't simply create it with the desired protection because creat 691 ANDs the mask with the user's default protection, which usually 692 denies some or all access to world. 693 */ 694 (void) chmod(file, FCMASK | 007); /* allow other users full access */ 695# endif /* VMS && !SECURE */ 696 697 return fd; 698} 699 700#ifdef MFLOPPY 701/* remove partial bonesfile in process of creation */ 702void 703cancel_bonesfile() 704{ 705 const char *tempname; 706 707 tempname = set_bonestemp_name(); 708 tempname = fqname(tempname, BONESPREFIX, 0); 709 (void) unlink(tempname); 710} 711#endif /* MFLOPPY */ 712 713/* move completed bones file to proper name */ 714void 715commit_bonesfile(lev) 716d_level *lev; 717{ 718 const char *fq_bones, *tempname; 719 int ret; 720 721 (void) set_bonesfile_name(bones, lev); 722 fq_bones = fqname(bones, BONESPREFIX, 0); 723 tempname = set_bonestemp_name(); 724 tempname = fqname(tempname, BONESPREFIX, 1); 725 726#if (defined(SYSV) && !defined(SVR4)) || defined(GENIX) 727 /* old SYSVs don't have rename. Some SVR3's may, but since they 728 * also have link/unlink, it doesn't matter. :-) 729 */ 730 (void) unlink(fq_bones); 731 ret = link(tempname, fq_bones); 732 ret += unlink(tempname); 733#else 734 ret = rename(tempname, fq_bones); 735#endif 736#ifdef WIZARD 737 if (wizard && ret != 0) 738 pline("couldn't rename %s to %s.", tempname, fq_bones); 739#endif 740} 741 742 743int 744open_bonesfile(lev, bonesid) 745d_level *lev; 746char **bonesid; 747{ 748 const char *fq_bones; 749 int fd; 750 751 *bonesid = set_bonesfile_name(bones, lev); 752 fq_bones = fqname(bones, BONESPREFIX, 0); 753 uncompress(fq_bones); /* no effect if nonexistent */ 754#ifdef MAC 755 fd = macopen(fq_bones, O_RDONLY | O_BINARY, BONE_TYPE); 756#else 757 fd = open(fq_bones, O_RDONLY | O_BINARY, 0); 758#endif 759 return fd; 760} 761 762 763int 764delete_bonesfile(lev) 765d_level *lev; 766{ 767 (void) set_bonesfile_name(bones, lev); 768 return !(unlink(fqname(bones, BONESPREFIX, 0)) < 0); 769} 770 771 772/* assume we're compressing the recently read or created bonesfile, so the 773 * file name is already set properly */ 774void 775compress_bonesfile() 776{ 777 compress(fqname(bones, BONESPREFIX, 0)); 778} 779 780/* ---------- END BONES FILE HANDLING ----------- */ 781 782 783/* ---------- BEGIN SAVE FILE HANDLING ----------- */ 784 785/* set savefile name in OS-dependent manner from pre-existing plname, 786 * avoiding troublesome characters */ 787void 788set_savefile_name() 789{ 790#if defined(WIN32) 791 char fnamebuf[BUFSZ], encodedfnamebuf[BUFSZ]; 792#endif 793#ifdef VMS 794 Sprintf(SAVEF, "[.save]%d%s", getuid(), plname); 795 regularize(SAVEF+7); 796 Strcat(SAVEF, ";1"); 797#else 798# if defined(MICRO) 799 Strcpy(SAVEF, SAVEP); 800# ifdef AMIGA 801 strncat(SAVEF, bbs_id, PATHLEN); 802# endif 803 { 804 int i = strlen(SAVEP); 805# ifdef AMIGA 806 /* plname has to share space with SAVEP and ".sav" */ 807 (void)strncat(SAVEF, plname, FILENAME - i - 4); 808# else 809 (void)strncat(SAVEF, plname, 8); 810# endif 811 regularize(SAVEF+i); 812 } 813 Strcat(SAVEF, ".sav"); 814# else 815# if defined(WIN32) 816 /* Obtain the name of the logged on user and incorporate 817 * it into the name. */ 818 Sprintf(fnamebuf, "%s-%s", get_username(0), plname); 819 (void)fname_encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.", 820 '%', fnamebuf, encodedfnamebuf, BUFSZ); 821 Sprintf(SAVEF, "%s.NetHack-saved-game", encodedfnamebuf); 822# else 823 Sprintf(SAVEF, "save/%d%s", (int)getuid(), plname); 824 regularize(SAVEF+5); /* avoid . or / in name */ 825# endif /* WIN32 */ 826# endif /* MICRO */ 827#endif /* VMS */ 828} 829 830#ifdef INSURANCE 831void 832save_savefile_name(fd) 833int fd; 834{ 835 (void) write(fd, (genericptr_t) SAVEF, sizeof(SAVEF)); 836} 837#endif 838 839 840#if defined(WIZARD) && !defined(MICRO) 841/* change pre-existing savefile name to indicate an error savefile */ 842void 843set_error_savefile() 844{ 845# ifdef VMS 846 { 847 char *semi_colon = rindex(SAVEF, ';'); 848 if (semi_colon) *semi_colon = '\0'; 849 } 850 Strcat(SAVEF, ".e;1"); 851# else 852# ifdef MAC 853 Strcat(SAVEF, "-e"); 854# else 855 Strcat(SAVEF, ".e"); 856# endif 857# endif 858} 859#endif 860 861 862/* create save file, overwriting one if it already exists */ 863int 864create_savefile() 865{ 866 const char *fq_save; 867 int fd; 868 869 fq_save = fqname(SAVEF, SAVEPREFIX, 0); 870#if defined(MICRO) || defined(WIN32) 871 fd = open(fq_save, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FCMASK); 872#else 873# ifdef MAC 874 fd = maccreat(fq_save, SAVE_TYPE); 875# else 876 fd = creat(fq_save, FCMASK); 877# endif 878# if defined(VMS) && !defined(SECURE) 879 /* 880 Make sure the save file is owned by the current process. That's 881 the default for non-privileged users, but for priv'd users the 882 file will be owned by the directory's owner instead of the user. 883 */ 884# ifdef getuid /*(see vmsunix.c)*/ 885# undef getuid 886# endif 887 (void) chown(fq_save, getuid(), getgid()); 888# endif /* VMS && !SECURE */ 889#endif /* MICRO */ 890 891 return fd; 892} 893 894 895/* open savefile for reading */ 896int 897open_savefile() 898{ 899#if 0 900 const char *fq_save; 901 int fd; 902 903 fq_save = fqname(SAVEF, SAVEPREFIX, 0); 904#ifdef MAC 905 fd = macopen(fq_save, O_RDONLY | O_BINARY, SAVE_TYPE); 906#else 907 fd = open(fq_save, O_RDONLY | O_BINARY, 0); 908#endif 909 return fd; 910#endif 911 // No savefiles in RefOS. 912 return -1; 913} 914 915 916/* delete savefile */ 917int 918delete_savefile() 919{ 920 (void) unlink(fqname(SAVEF, SAVEPREFIX, 0)); 921 return 0; /* for restore_saved_game() (ex-xxxmain.c) test */ 922} 923 924 925/* try to open up a save file and prepare to restore it */ 926int 927restore_saved_game() 928{ 929 const char *fq_save; 930 int fd; 931 932 set_savefile_name(); 933#ifdef MFLOPPY 934 if (!saveDiskPrompt(1)) 935 return -1; 936#endif /* MFLOPPY */ 937 fq_save = fqname(SAVEF, SAVEPREFIX, 0); 938 939 uncompress(fq_save); 940 if ((fd = open_savefile()) < 0) return fd; 941 942 if (!uptodate(fd, fq_save)) { 943 (void) close(fd), fd = -1; 944 (void) delete_savefile(); 945 } 946 return fd; 947} 948 949#if defined(UNIX) && defined(QT_GRAPHICS) 950/*ARGSUSED*/ 951static char* 952plname_from_file(filename) 953const char* filename; 954{ 955#ifdef STORE_PLNAME_IN_FILE 956 int fd; 957 char* result = 0; 958 959 Strcpy(SAVEF,filename); 960#ifdef COMPRESS_EXTENSION 961 SAVEF[strlen(SAVEF)-strlen(COMPRESS_EXTENSION)] = '\0'; 962#endif 963 uncompress(SAVEF); 964 if ((fd = open_savefile()) >= 0) { 965 if (uptodate(fd, filename)) { 966 char tplname[PL_NSIZ]; 967 mread(fd, (genericptr_t) tplname, PL_NSIZ); 968 result = strdup(tplname); 969 } 970 (void) close(fd); 971 } 972 compress(SAVEF); 973 974 return result; 975#else 976# if defined(UNIX) && defined(QT_GRAPHICS) 977 /* Name not stored in save file, so we have to extract it from 978 the filename, which loses information 979 (eg. "/", "_", and "." characters are lost. */ 980 int k; 981 int uid; 982 char name[64]; /* more than PL_NSIZ */ 983#ifdef COMPRESS_EXTENSION 984#define EXTSTR COMPRESS_EXTENSION 985#else 986#define EXTSTR "" 987#endif 988 if ( sscanf( filename, "%*[^/]/%d%63[^.]" EXTSTR, &uid, name ) == 2 ) { 989#undef EXTSTR 990 /* "_" most likely means " ", which certainly looks nicer */ 991 for (k=0; name[k]; k++) 992 if ( name[k]=='_' ) 993 name[k]=' '; 994 return strdup(name); 995 } else 996# endif 997 { 998 return 0; 999 } 1000#endif 1001} 1002#endif /* defined(UNIX) && defined(QT_GRAPHICS) */ 1003 1004char** 1005get_saved_games() 1006{ 1007#if defined(UNIX) && defined(QT_GRAPHICS) 1008 int myuid=getuid(); 1009 struct dirent **namelist; 1010 int n = scandir("save", &namelist, 0, alphasort);; 1011 if ( n > 0 ) { 1012 int i,j=0; 1013 char** result = (char**)alloc((n+1)*sizeof(char*)); /* at most */ 1014 for (i=0; i<n; i++) { 1015 int uid; 1016 char name[64]; /* more than PL_NSIZ */ 1017 if ( sscanf( namelist[i]->d_name, "%d%63s", &uid, name ) == 2 ) { 1018 if ( uid == myuid ) { 1019 char filename[BUFSZ]; 1020 char* r; 1021 Sprintf(filename,"save/%d%s",uid,name); 1022 r = plname_from_file(filename); 1023 if ( r ) 1024 result[j++] = r; 1025 } 1026 } 1027 } 1028 result[j++] = 0; 1029 return result; 1030 } else 1031#endif 1032 { 1033 return 0; 1034 } 1035} 1036 1037void 1038free_saved_games(saved) 1039char** saved; 1040{ 1041 if ( saved ) { 1042 int i=0; 1043 while (saved[i]) free((genericptr_t)saved[i++]); 1044 free((genericptr_t)saved); 1045 } 1046} 1047 1048 1049/* ---------- END SAVE FILE HANDLING ----------- */ 1050 1051 1052/* ---------- BEGIN FILE COMPRESSION HANDLING ----------- */ 1053 1054#ifdef COMPRESS 1055 1056STATIC_OVL void 1057redirect(filename, mode, stream, uncomp) 1058const char *filename, *mode; 1059FILE *stream; 1060boolean uncomp; 1061{ 1062 if (freopen(filename, mode, stream) == (FILE *)0) { 1063 (void) fprintf(stderr, "freopen of %s for %scompress failed\n", 1064 filename, uncomp ? "un" : ""); 1065 terminate(EXIT_FAILURE); 1066 } 1067} 1068 1069/* 1070 * using system() is simpler, but opens up security holes and causes 1071 * problems on at least Interactive UNIX 3.0.1 (SVR3.2), where any 1072 * setuid is renounced by /bin/sh, so the files cannot be accessed. 1073 * 1074 * cf. child() in unixunix.c. 1075 */ 1076STATIC_OVL void 1077docompress_file(filename, uncomp) 1078const char *filename; 1079boolean uncomp; 1080{ 1081 char cfn[80]; 1082 FILE *cf; 1083 const char *args[10]; 1084# ifdef COMPRESS_OPTIONS 1085 char opts[80]; 1086# endif 1087 int i = 0; 1088 int f; 1089# ifdef TTY_GRAPHICS 1090 boolean istty = !strncmpi(windowprocs.name, "tty", 3); 1091# endif 1092 1093 Strcpy(cfn, filename); 1094# ifdef COMPRESS_EXTENSION 1095 Strcat(cfn, COMPRESS_EXTENSION); 1096# endif 1097 /* when compressing, we know the file exists */ 1098 if (uncomp) { 1099 if ((cf = fopen(cfn, RDBMODE)) == (FILE *)0) 1100 return; 1101 (void) fclose(cf); 1102 } 1103 1104 args[0] = COMPRESS; 1105 if (uncomp) args[++i] = "-d"; /* uncompress */ 1106# ifdef COMPRESS_OPTIONS 1107 { 1108 /* we can't guarantee there's only one additional option, sigh */ 1109 char *opt; 1110 boolean inword = FALSE; 1111 1112 Strcpy(opts, COMPRESS_OPTIONS); 1113 opt = opts; 1114 while (*opt) { 1115 if ((*opt == ' ') || (*opt == '\t')) { 1116 if (inword) { 1117 *opt = '\0'; 1118 inword = FALSE; 1119 } 1120 } else if (!inword) { 1121 args[++i] = opt; 1122 inword = TRUE; 1123 } 1124 opt++; 1125 } 1126 } 1127# endif 1128 args[++i] = (char *)0; 1129 1130# ifdef TTY_GRAPHICS 1131 /* If we don't do this and we are right after a y/n question *and* 1132 * there is an error message from the compression, the 'y' or 'n' can 1133 * end up being displayed after the error message. 1134 */ 1135 if (istty) 1136 mark_synch(); 1137# endif 1138 f = fork(); 1139 if (f == 0) { /* child */ 1140# ifdef TTY_GRAPHICS 1141 /* any error messages from the compression must come out after 1142 * the first line, because the more() to let the user read 1143 * them will have to clear the first line. This should be 1144 * invisible if there are no error messages. 1145 */ 1146 if (istty) 1147 raw_print(""); 1148# endif 1149 /* run compressor without privileges, in case other programs 1150 * have surprises along the line of gzip once taking filenames 1151 * in GZIP. 1152 */ 1153 /* assume all compressors will compress stdin to stdout 1154 * without explicit filenames. this is true of at least 1155 * compress and gzip, those mentioned in config.h. 1156 */ 1157 if (uncomp) { 1158 redirect(cfn, RDBMODE, stdin, uncomp); 1159 redirect(filename, WRBMODE, stdout, uncomp); 1160 } else { 1161 redirect(filename, RDBMODE, stdin, uncomp); 1162 redirect(cfn, WRBMODE, stdout, uncomp); 1163 } 1164 (void) setgid(getgid()); 1165 (void) setuid(getuid()); 1166 (void) execv(args[0], (char *const *) args); 1167 perror((char *)0); 1168 (void) fprintf(stderr, "Exec to %scompress %s failed.\n", 1169 uncomp ? "un" : "", filename); 1170 terminate(EXIT_FAILURE); 1171 } else if (f == -1) { 1172 perror((char *)0); 1173 pline("Fork to %scompress %s failed.", 1174 uncomp ? "un" : "", filename); 1175 return; 1176 } 1177 (void) signal(SIGINT, SIG_IGN); 1178 (void) signal(SIGQUIT, SIG_IGN); 1179 (void) wait((int *)&i); 1180 (void) signal(SIGINT, (SIG_RET_TYPE) done1); 1181# ifdef WIZARD 1182 if (wizard) (void) signal(SIGQUIT, SIG_DFL); 1183# endif 1184 if (i == 0) { 1185 /* (un)compress succeeded: remove file left behind */ 1186 if (uncomp) 1187 (void) unlink(cfn); 1188 else 1189 (void) unlink(filename); 1190 } else { 1191 /* (un)compress failed; remove the new, bad file */ 1192 if (uncomp) { 1193 raw_printf("Unable to uncompress %s", filename); 1194 (void) unlink(filename); 1195 } else { 1196 /* no message needed for compress case; life will go on */ 1197 (void) unlink(cfn); 1198 } 1199#ifdef TTY_GRAPHICS 1200 /* Give them a chance to read any error messages from the 1201 * compression--these would go to stdout or stderr and would get 1202 * overwritten only in tty mode. It's still ugly, since the 1203 * messages are being written on top of the screen, but at least 1204 * the user can read them. 1205 */ 1206 if (istty) 1207 { 1208 clear_nhwindow(WIN_MESSAGE); 1209 more(); 1210 /* No way to know if this is feasible */ 1211 /* doredraw(); */ 1212 } 1213#endif 1214 } 1215} 1216#endif /* COMPRESS */ 1217 1218/* compress file */ 1219void 1220compress(filename) 1221const char *filename; 1222{ 1223#ifndef COMPRESS 1224#if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__) 1225# pragma unused(filename) 1226#endif 1227#else 1228 docompress_file(filename, FALSE); 1229#endif 1230} 1231 1232 1233/* uncompress file if it exists */ 1234void 1235uncompress(filename) 1236const char *filename; 1237{ 1238#ifndef COMPRESS 1239#if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__) 1240# pragma unused(filename) 1241#endif 1242#else 1243 docompress_file(filename, TRUE); 1244#endif 1245} 1246 1247/* ---------- END FILE COMPRESSION HANDLING ----------- */ 1248 1249 1250/* ---------- BEGIN FILE LOCKING HANDLING ----------- */ 1251 1252static int nesting = 0; 1253 1254#ifdef NO_FILE_LINKS /* implies UNIX */ 1255static int lockfd; /* for lock_file() to pass to unlock_file() */ 1256#endif 1257 1258#define HUP if (!program_state.done_hup) 1259 1260STATIC_OVL char * 1261make_lockname(filename, lockname) 1262const char *filename; 1263char *lockname; 1264{ 1265#if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__) 1266# pragma unused(filename,lockname) 1267 return (char*)0; 1268#else 1269# if defined(UNIX) || defined(VMS) || defined(AMIGA) || defined(WIN32) || defined(MSDOS) 1270# ifdef NO_FILE_LINKS 1271 Strcpy(lockname, LOCKDIR); 1272 Strcat(lockname, "/"); 1273 Strcat(lockname, filename); 1274# else 1275 Strcpy(lockname, filename); 1276# endif 1277# ifdef VMS 1278 { 1279 char *semi_colon = rindex(lockname, ';'); 1280 if (semi_colon) *semi_colon = '\0'; 1281 } 1282 Strcat(lockname, ".lock;1"); 1283# else 1284 Strcat(lockname, "_lock"); 1285# endif 1286 return lockname; 1287# else 1288 lockname[0] = '\0'; 1289 return (char*)0; 1290# endif /* UNIX || VMS || AMIGA || WIN32 || MSDOS */ 1291#endif 1292} 1293 1294 1295/* lock a file */ 1296boolean 1297lock_file(filename, whichprefix, retryct) 1298const char *filename; 1299int whichprefix; 1300int retryct; 1301{ 1302#if 0 1303#if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__) 1304# pragma unused(filename, retryct) 1305#endif 1306 char locknambuf[BUFSZ]; 1307 const char *lockname; 1308 1309 nesting++; 1310 if (nesting > 1) { 1311 impossible("TRIED TO NEST LOCKS"); 1312 return TRUE; 1313 } 1314 1315 lockname = make_lockname(filename, locknambuf); 1316 filename = fqname(filename, whichprefix, 0); 1317#ifndef NO_FILE_LINKS /* LOCKDIR should be subsumed by LOCKPREFIX */ 1318 lockname = fqname(lockname, LOCKPREFIX, 2); 1319#endif 1320 1321#if defined(UNIX) || defined(VMS) 1322# ifdef NO_FILE_LINKS 1323 while ((lockfd = open(lockname, O_RDWR|O_CREAT|O_EXCL, 0666)) == -1) { 1324# else 1325 while (link(filename, lockname) == -1) { 1326# endif 1327 register int errnosv = errno; 1328 1329 switch (errnosv) { /* George Barbanis */ 1330 case EEXIST: 1331 if (retryct--) { 1332 HUP raw_printf( 1333 "Waiting for access to %s. (%d retries left).", 1334 filename, retryct); 1335# if defined(SYSV) || defined(ULTRIX) || defined(VMS) 1336 (void) 1337# endif 1338 sleep(1); 1339 } else { 1340 HUP (void) raw_print("I give up. Sorry."); 1341 HUP raw_printf("Perhaps there is an old %s around?", 1342 lockname); 1343 nesting--; 1344 return FALSE; 1345 } 1346 1347 break; 1348 case ENOENT: 1349 HUP raw_printf("Can't find file %s to lock!", filename); 1350 nesting--; 1351 return FALSE; 1352 case EACCES: 1353 HUP raw_printf("No write permission to lock %s!", filename); 1354 nesting--; 1355 return FALSE; 1356# ifdef VMS /* c__translate(vmsfiles.c) */ 1357 case EPERM: 1358 /* could be misleading, but usually right */ 1359 HUP raw_printf("Can't lock %s due to directory protection.", 1360 filename); 1361 nesting--; 1362 return FALSE; 1363# endif 1364 default: 1365 HUP perror(lockname); 1366 HUP raw_printf( 1367 "Cannot lock %s for unknown reason (%d).", 1368 filename, errnosv); 1369 nesting--; 1370 return FALSE; 1371 } 1372 1373 } 1374#endif /* UNIX || VMS */ 1375 1376#if defined(AMIGA) || defined(WIN32) || defined(MSDOS) 1377# ifdef AMIGA 1378#define OPENFAILURE(fd) (!fd) 1379 lockptr = 0; 1380# else 1381#define OPENFAILURE(fd) (fd < 0) 1382 lockptr = -1; 1383# endif 1384 while (--retryct && OPENFAILURE(lockptr)) { 1385# if defined(WIN32) && !defined(WIN_CE) 1386 lockptr = sopen(lockname, O_RDWR|O_CREAT, SH_DENYRW, S_IWRITE); 1387# else 1388 (void)DeleteFile(lockname); /* in case dead process was here first */ 1389# ifdef AMIGA 1390 lockptr = Open(lockname,MODE_NEWFILE); 1391# else 1392 lockptr = open(lockname, O_RDWR|O_CREAT|O_EXCL, S_IWRITE); 1393# endif 1394# endif 1395 if (OPENFAILURE(lockptr)) { 1396 raw_printf("Waiting for access to %s. (%d retries left).", 1397 filename, retryct); 1398 Delay(50); 1399 } 1400 } 1401 if (!retryct) { 1402 raw_printf("I give up. Sorry."); 1403 nesting--; 1404 return FALSE; 1405 } 1406#endif /* AMIGA || WIN32 || MSDOS */ 1407#endif 1408 1409 // RefOS file creation locking unsupported. 1410 return TRUE; 1411} 1412 1413 1414#ifdef VMS /* for unlock_file, use the unlink() routine in vmsunix.c */ 1415# ifdef unlink 1416# undef unlink 1417# endif 1418# define unlink(foo) vms_unlink(foo) 1419#endif 1420 1421/* unlock file, which must be currently locked by lock_file */ 1422void 1423unlock_file(filename) 1424const char *filename; 1425#if defined(macintosh) && (defined(__SC__) || defined(__MRC__)) 1426# pragma unused(filename) 1427#endif 1428{ 1429 #if 0 1430 char locknambuf[BUFSZ]; 1431 const char *lockname; 1432 1433 if (nesting == 1) { 1434 lockname = make_lockname(filename, locknambuf); 1435#ifndef NO_FILE_LINKS /* LOCKDIR should be subsumed by LOCKPREFIX */ 1436 lockname = fqname(lockname, LOCKPREFIX, 2); 1437#endif 1438 1439#if defined(UNIX) || defined(VMS) 1440 if (unlink(lockname) < 0) 1441 HUP raw_printf("Can't unlink %s.", lockname); 1442# ifdef NO_FILE_LINKS 1443 (void) close(lockfd); 1444# endif 1445 1446#endif /* UNIX || VMS */ 1447 1448#if defined(AMIGA) || defined(WIN32) || defined(MSDOS) 1449 if (lockptr) Close(lockptr); 1450 DeleteFile(lockname); 1451 lockptr = 0; 1452#endif /* AMIGA || WIN32 || MSDOS */ 1453 } 1454 1455 nesting--; 1456 #endif 1457 1458 // RefOS file creation locking unsupported. 1459 return; 1460} 1461 1462/* ---------- END FILE LOCKING HANDLING ----------- */ 1463 1464 1465/* ---------- BEGIN CONFIG FILE HANDLING ----------- */ 1466 1467const char *configfile = 1468#if 0 1469#ifdef UNIX 1470 ".nethackrc"; 1471#else 1472# if defined(MAC) || defined(__BEOS__) 1473 "NetHack Defaults"; 1474# else 1475# if defined(MSDOS) || defined(WIN32) 1476 "defaults.nh"; 1477# else 1478 "NetHack.cnf"; 1479# endif 1480# endif 1481#endif 1482#endif 1483 "nethackrc"; // RefOS nethackrc location. 1484 1485 1486#ifdef MSDOS 1487/* conflict with speed-dial under windows 1488 * for XXX.cnf file so support of NetHack.cnf 1489 * is for backward compatibility only. 1490 * Preferred name (and first tried) is now defaults.nh but 1491 * the game will try the old name if there 1492 * is no defaults.nh. 1493 */ 1494const char *backward_compat_configfile = "nethack.cnf"; 1495#endif 1496 1497#ifndef MFLOPPY 1498#define fopenp fopen 1499#endif 1500 1501STATIC_OVL FILE * 1502fopen_config_file(filename) 1503const char *filename; 1504{ 1505 FILE *fp; 1506#if defined(UNIX) || defined(VMS) 1507 char tmp_config[BUFSZ]; 1508 char *envp; 1509#endif 1510 1511 /* "filename" is an environment variable, so it should hang around */ 1512 /* if set, it is expected to be a full path name (if relevant) */ 1513 if (filename) { 1514#ifdef UNIX 1515 if (access(filename, 4) == -1) { 1516 /* 4 is R_OK on newer systems */ 1517 /* nasty sneaky attempt to read file through 1518 * NetHack's setuid permissions -- this is the only 1519 * place a file name may be wholly under the player's 1520 * control 1521 */ 1522 raw_printf("Access to %s denied (%d).", 1523 filename, errno); 1524 wait_synch(); 1525 /* fall through to standard names */ 1526 } else 1527#endif 1528 if ((fp = fopenp(filename, "r")) != (FILE *)0) { 1529 configfile = filename; 1530 return(fp); 1531#if defined(UNIX) || defined(VMS) 1532 } else { 1533 /* access() above probably caught most problems for UNIX */ 1534 raw_printf("Couldn't open requested config file %s (%d).", 1535 filename, errno); 1536 wait_synch(); 1537 /* fall through to standard names */ 1538#endif 1539 } 1540 } 1541 1542#if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32) 1543 if ((fp = fopenp(fqname(configfile, CONFIGPREFIX, 0), "r")) 1544 != (FILE *)0) 1545 return(fp); 1546# ifdef MSDOS 1547 else if ((fp = fopenp(fqname(backward_compat_configfile, 1548 CONFIGPREFIX, 0), "r")) != (FILE *)0) 1549 return(fp); 1550# endif 1551#else 1552 /* constructed full path names don't need fqname() */ 1553# ifdef VMS 1554 if ((fp = fopenp(fqname("nethackini", CONFIGPREFIX, 0), "r")) 1555 != (FILE *)0) { 1556 configfile = "nethackini"; 1557 return(fp); 1558 } 1559 if ((fp = fopenp("sys$login:nethack.ini", "r")) != (FILE *)0) { 1560 configfile = "nethack.ini"; 1561 return(fp); 1562 } 1563 1564 envp = nh_getenv("HOME"); 1565 if (!envp) 1566 Strcpy(tmp_config, "NetHack.cnf"); 1567 else 1568 Sprintf(tmp_config, "%s%s", envp, "NetHack.cnf"); 1569 if ((fp = fopenp(tmp_config, "r")) != (FILE *)0) 1570 return(fp); 1571# else /* should be only UNIX left */ 1572 envp = nh_getenv("HOME"); 1573 if (!envp) 1574 Strcpy(tmp_config, configfile); 1575 else 1576 Sprintf(tmp_config, "%s/%s", envp, configfile); 1577 if ((fp = fopenp(tmp_config, "r")) != (FILE *)0) 1578 return(fp); 1579# if defined(__APPLE__) 1580 /* try an alternative */ 1581 if (envp) { 1582 Sprintf(tmp_config, "%s/%s", envp, "Library/Preferences/NetHack Defaults"); 1583 if ((fp = fopenp(tmp_config, "r")) != (FILE *)0) 1584 return(fp); 1585 Sprintf(tmp_config, "%s/%s", envp, "Library/Preferences/NetHack Defaults.txt"); 1586 if ((fp = fopenp(tmp_config, "r")) != (FILE *)0) 1587 return(fp); 1588 } 1589# endif 1590 if (errno != ENOENT) { 1591 char *details; 1592 1593 /* e.g., problems when setuid NetHack can't search home 1594 * directory restricted to user */ 1595 1596#if defined (NHSTDC) && !defined(NOTSTDC) 1597 if ((details = strerror(errno)) == 0) 1598#endif 1599 details = ""; 1600 raw_printf("Couldn't open default config file %s %s(%d).", 1601 tmp_config, details, errno); 1602 wait_synch(); 1603 } 1604# endif 1605#endif 1606 return (FILE *)0; 1607 1608} 1609 1610 1611/* 1612 * Retrieve a list of integers from a file into a uchar array. 1613 * 1614 * NOTE: zeros are inserted unless modlist is TRUE, in which case the list 1615 * location is unchanged. Callers must handle zeros if modlist is FALSE. 1616 */ 1617STATIC_OVL int 1618get_uchars(fp, buf, bufp, list, modlist, size, name) 1619 FILE *fp; /* input file pointer */ 1620 char *buf; /* read buffer, must be of size BUFSZ */ 1621 char *bufp; /* current pointer */ 1622 uchar *list; /* return list */ 1623 boolean modlist; /* TRUE: list is being modified in place */ 1624 int size; /* return list size */ 1625 const char *name; /* name of option for error message */ 1626{ 1627 unsigned int num = 0; 1628 int count = 0; 1629 boolean havenum = FALSE; 1630 1631 while (1) { 1632 switch(*bufp) { 1633 case ' ': case '\0': 1634 case '\t': case '\n': 1635 if (havenum) { 1636 /* if modifying in place, don't insert zeros */ 1637 if (num || !modlist) list[count] = num; 1638 count++; 1639 num = 0; 1640 havenum = FALSE; 1641 } 1642 if (count == size || !*bufp) return count; 1643 bufp++; 1644 break; 1645 1646 case '0': case '1': case '2': case '3': 1647 case '4': case '5': case '6': case '7': 1648 case '8': case '9': 1649 havenum = TRUE; 1650 num = num*10 + (*bufp-'0'); 1651 bufp++; 1652 break; 1653 1654 case '\\': 1655 if (fp == (FILE *)0) 1656 goto gi_error; 1657 do { 1658 if (!fgets(buf, BUFSZ, fp)) goto gi_error; 1659 } while (buf[0] == '#'); 1660 bufp = buf; 1661 break; 1662 1663 default: 1664gi_error: 1665 raw_printf("Syntax error in %s", name); 1666 wait_synch(); 1667 return count; 1668 } 1669 } 1670 /*NOTREACHED*/ 1671} 1672 1673#ifdef NOCWD_ASSUMPTIONS 1674STATIC_OVL void 1675adjust_prefix(bufp, prefixid) 1676char *bufp; 1677int prefixid; 1678{ 1679 char *ptr; 1680 1681 if (!bufp) return; 1682 /* Backward compatibility, ignore trailing ;n */ 1683 if ((ptr = index(bufp, ';')) != 0) *ptr = '\0'; 1684 if (strlen(bufp) > 0) { 1685 fqn_prefix[prefixid] = (char *)alloc(strlen(bufp)+2); 1686 Strcpy(fqn_prefix[prefixid], bufp); 1687 append_slash(fqn_prefix[prefixid]); 1688 } 1689} 1690#endif 1691 1692#define match_varname(INP,NAM,LEN) match_optname(INP, NAM, LEN, TRUE) 1693 1694/*ARGSUSED*/ 1695int 1696parse_config_line(fp, buf, tmp_ramdisk, tmp_levels) 1697FILE *fp; 1698char *buf; 1699char *tmp_ramdisk; 1700char *tmp_levels; 1701{ 1702#if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__) 1703# pragma unused(tmp_ramdisk,tmp_levels) 1704#endif 1705 char *bufp, *altp; 1706 uchar translate[MAXPCHARS]; 1707 int len; 1708 1709 if (*buf == '#') 1710 return 1; 1711 1712 /* remove trailing whitespace */ 1713 bufp = eos(buf); 1714 while (--bufp > buf && isspace(*bufp)) 1715 continue; 1716 1717 if (bufp <= buf) 1718 return 1; /* skip all-blank lines */ 1719 else 1720 *(bufp + 1) = '\0'; /* terminate line */ 1721 1722 /* find the '=' or ':' */ 1723 bufp = index(buf, '='); 1724 altp = index(buf, ':'); 1725 if (!bufp || (altp && altp < bufp)) bufp = altp; 1726 if (!bufp) return 0; 1727 1728 /* skip whitespace between '=' and value */ 1729 do { ++bufp; } while (isspace(*bufp)); 1730 1731 /* Go through possible variables */ 1732 /* some of these (at least LEVELS and SAVE) should now set the 1733 * appropriate fqn_prefix[] rather than specialized variables 1734 */ 1735 if (match_varname(buf, "OPTIONS", 4)) { 1736 parseoptions(bufp, TRUE, TRUE); 1737 if (plname[0]) /* If a name was given */ 1738 plnamesuffix(); /* set the character class */ 1739#ifdef AUTOPICKUP_EXCEPTIONS 1740 } else if (match_varname(buf, "AUTOPICKUP_EXCEPTION", 5)) { 1741 add_autopickup_exception(bufp); 1742#endif 1743#ifdef NOCWD_ASSUMPTIONS 1744 } else if (match_varname(buf, "HACKDIR", 4)) { 1745 adjust_prefix(bufp, HACKPREFIX); 1746 } else if (match_varname(buf, "LEVELDIR", 4) || 1747 match_varname(buf, "LEVELS", 4)) { 1748 adjust_prefix(bufp, LEVELPREFIX); 1749 } else if (match_varname(buf, "SAVEDIR", 4)) { 1750 adjust_prefix(bufp, SAVEPREFIX); 1751 } else if (match_varname(buf, "BONESDIR", 5)) { 1752 adjust_prefix(bufp, BONESPREFIX); 1753 } else if (match_varname(buf, "DATADIR", 4)) { 1754 adjust_prefix(bufp, DATAPREFIX); 1755 } else if (match_varname(buf, "SCOREDIR", 4)) { 1756 adjust_prefix(bufp, SCOREPREFIX); 1757 } else if (match_varname(buf, "LOCKDIR", 4)) { 1758 adjust_prefix(bufp, LOCKPREFIX); 1759 } else if (match_varname(buf, "CONFIGDIR", 4)) { 1760 adjust_prefix(bufp, CONFIGPREFIX); 1761 } else if (match_varname(buf, "TROUBLEDIR", 4)) { 1762 adjust_prefix(bufp, TROUBLEPREFIX); 1763#else /*NOCWD_ASSUMPTIONS*/ 1764# ifdef MICRO 1765 } else if (match_varname(buf, "HACKDIR", 4)) { 1766 (void) strncpy(hackdir, bufp, PATHLEN-1); 1767# ifdef MFLOPPY 1768 } else if (match_varname(buf, "RAMDISK", 3)) { 1769 /* The following ifdef is NOT in the wrong 1770 * place. For now, we accept and silently 1771 * ignore RAMDISK */ 1772# ifndef AMIGA 1773 (void) strncpy(tmp_ramdisk, bufp, PATHLEN-1); 1774# endif 1775# endif 1776 } else if (match_varname(buf, "LEVELS", 4)) { 1777 (void) strncpy(tmp_levels, bufp, PATHLEN-1); 1778 1779 } else if (match_varname(buf, "SAVE", 4)) { 1780# ifdef MFLOPPY 1781 extern int saveprompt; 1782# endif 1783 char *ptr; 1784 if ((ptr = index(bufp, ';')) != 0) { 1785 *ptr = '\0'; 1786# ifdef MFLOPPY 1787 if (*(ptr+1) == 'n' || *(ptr+1) == 'N') { 1788 saveprompt = FALSE; 1789 } 1790# endif 1791 } 1792# ifdef MFLOPPY 1793 else 1794 saveprompt = flags.asksavedisk; 1795# endif 1796 1797 (void) strncpy(SAVEP, bufp, SAVESIZE-1); 1798 append_slash(SAVEP); 1799# endif /* MICRO */ 1800#endif /*NOCWD_ASSUMPTIONS*/ 1801 1802 } else if (match_varname(buf, "NAME", 4)) { 1803 (void) strncpy(plname, bufp, PL_NSIZ-1); 1804 plnamesuffix(); 1805 } else if (match_varname(buf, "ROLE", 4) || 1806 match_varname(buf, "CHARACTER", 4)) { 1807 if ((len = str2role(bufp)) >= 0) 1808 flags.initrole = len; 1809 } else if (match_varname(buf, "DOGNAME", 3)) { 1810 (void) strncpy(dogname, bufp, PL_PSIZ-1); 1811 } else if (match_varname(buf, "CATNAME", 3)) { 1812 (void) strncpy(catname, bufp, PL_PSIZ-1); 1813 1814 } else if (match_varname(buf, "BOULDER", 3)) { 1815 (void) get_uchars(fp, buf, bufp, &iflags.bouldersym, TRUE, 1816 1, "BOULDER"); 1817 } else if (match_varname(buf, "GRAPHICS", 4)) { 1818 len = get_uchars(fp, buf, bufp, translate, FALSE, 1819 MAXPCHARS, "GRAPHICS"); 1820 assign_graphics(translate, len, MAXPCHARS, 0); 1821 } else if (match_varname(buf, "DUNGEON", 4)) { 1822 len = get_uchars(fp, buf, bufp, translate, FALSE, 1823 MAXDCHARS, "DUNGEON"); 1824 assign_graphics(translate, len, MAXDCHARS, 0); 1825 } else if (match_varname(buf, "TRAPS", 4)) { 1826 len = get_uchars(fp, buf, bufp, translate, FALSE, 1827 MAXTCHARS, "TRAPS"); 1828 assign_graphics(translate, len, MAXTCHARS, MAXDCHARS); 1829 } else if (match_varname(buf, "EFFECTS", 4)) { 1830 len = get_uchars(fp, buf, bufp, translate, FALSE, 1831 MAXECHARS, "EFFECTS"); 1832 assign_graphics(translate, len, MAXECHARS, MAXDCHARS+MAXTCHARS); 1833 1834 } else if (match_varname(buf, "OBJECTS", 3)) { 1835 /* oc_syms[0] is the RANDOM object, unused */ 1836 (void) get_uchars(fp, buf, bufp, &(oc_syms[1]), TRUE, 1837 MAXOCLASSES-1, "OBJECTS"); 1838 } else if (match_varname(buf, "MONSTERS", 3)) { 1839 /* monsyms[0] is unused */ 1840 (void) get_uchars(fp, buf, bufp, &(monsyms[1]), TRUE, 1841 MAXMCLASSES-1, "MONSTERS"); 1842 } else if (match_varname(buf, "WARNINGS", 5)) { 1843 (void) get_uchars(fp, buf, bufp, translate, FALSE, 1844 WARNCOUNT, "WARNINGS"); 1845 assign_warnings(translate); 1846#ifdef WIZARD 1847 } else if (match_varname(buf, "WIZKIT", 6)) { 1848 (void) strncpy(wizkit, bufp, WIZKIT_MAX-1); 1849#endif 1850#ifdef AMIGA 1851 } else if (match_varname(buf, "FONT", 4)) { 1852 char *t; 1853 1854 if( t = strchr( buf+5, ':' ) ) 1855 { 1856 *t = 0; 1857 amii_set_text_font( buf+5, atoi( t + 1 ) ); 1858 *t = ':'; 1859 } 1860 } else if (match_varname(buf, "PATH", 4)) { 1861 (void) strncpy(PATH, bufp, PATHLEN-1); 1862 } else if (match_varname(buf, "DEPTH", 5)) { 1863 extern int amii_numcolors; 1864 int val = atoi( bufp ); 1865 amii_numcolors = 1L << min( DEPTH, val ); 1866 } else if (match_varname(buf, "DRIPENS", 7)) { 1867 int i, val; 1868 char *t; 1869 for (i = 0, t = strtok(bufp, ",/"); t != (char *)0; 1870 i < 20 && (t = strtok((char*)0, ",/")), ++i) { 1871 sscanf(t, "%d", &val ); 1872 flags.amii_dripens[i] = val; 1873 } 1874 } else if (match_varname(buf, "SCREENMODE", 10 )) { 1875 extern long amii_scrnmode; 1876 if (!stricmp(bufp,"req")) 1877 amii_scrnmode = 0xffffffff; /* Requester */ 1878 else if( sscanf(bufp, "%x", &amii_scrnmode) != 1 ) 1879 amii_scrnmode = 0; 1880 } else if (match_varname(buf, "MSGPENS", 7)) { 1881 extern int amii_msgAPen, amii_msgBPen; 1882 char *t = strtok(bufp, ",/"); 1883 if( t ) 1884 { 1885 sscanf(t, "%d", &amii_msgAPen); 1886 if( t = strtok((char*)0, ",/") ) 1887 sscanf(t, "%d", &amii_msgBPen); 1888 } 1889 } else if (match_varname(buf, "TEXTPENS", 8)) { 1890 extern int amii_textAPen, amii_textBPen; 1891 char *t = strtok(bufp, ",/"); 1892 if( t ) 1893 { 1894 sscanf(t, "%d", &amii_textAPen); 1895 if( t = strtok((char*)0, ",/") ) 1896 sscanf(t, "%d", &amii_textBPen); 1897 } 1898 } else if (match_varname(buf, "MENUPENS", 8)) { 1899 extern int amii_menuAPen, amii_menuBPen; 1900 char *t = strtok(bufp, ",/"); 1901 if( t ) 1902 { 1903 sscanf(t, "%d", &amii_menuAPen); 1904 if( t = strtok((char*)0, ",/") ) 1905 sscanf(t, "%d", &amii_menuBPen); 1906 } 1907 } else if (match_varname(buf, "STATUSPENS", 10)) { 1908 extern int amii_statAPen, amii_statBPen; 1909 char *t = strtok(bufp, ",/"); 1910 if( t ) 1911 { 1912 sscanf(t, "%d", &amii_statAPen); 1913 if( t = strtok((char*)0, ",/") ) 1914 sscanf(t, "%d", &amii_statBPen); 1915 } 1916 } else if (match_varname(buf, "OTHERPENS", 9)) { 1917 extern int amii_otherAPen, amii_otherBPen; 1918 char *t = strtok(bufp, ",/"); 1919 if( t ) 1920 { 1921 sscanf(t, "%d", &amii_otherAPen); 1922 if( t = strtok((char*)0, ",/") ) 1923 sscanf(t, "%d", &amii_otherBPen); 1924 } 1925 } else if (match_varname(buf, "PENS", 4)) { 1926 extern unsigned short amii_init_map[ AMII_MAXCOLORS ]; 1927 int i; 1928 char *t; 1929 1930 for (i = 0, t = strtok(bufp, ",/"); 1931 i < AMII_MAXCOLORS && t != (char *)0; 1932 t = strtok((char *)0, ",/"), ++i) 1933 { 1934 sscanf(t, "%hx", &amii_init_map[i]); 1935 } 1936 amii_setpens( amii_numcolors = i ); 1937 } else if (match_varname(buf, "FGPENS", 6)) { 1938 extern int foreg[ AMII_MAXCOLORS ]; 1939 int i; 1940 char *t; 1941 1942 for (i = 0, t = strtok(bufp, ",/"); 1943 i < AMII_MAXCOLORS && t != (char *)0; 1944 t = strtok((char *)0, ",/"), ++i) 1945 { 1946 sscanf(t, "%d", &foreg[i]); 1947 } 1948 } else if (match_varname(buf, "BGPENS", 6)) { 1949 extern int backg[ AMII_MAXCOLORS ]; 1950 int i; 1951 char *t; 1952 1953 for (i = 0, t = strtok(bufp, ",/"); 1954 i < AMII_MAXCOLORS && t != (char *)0; 1955 t = strtok((char *)0, ",/"), ++i) 1956 { 1957 sscanf(t, "%d", &backg[i]); 1958 } 1959#endif 1960#ifdef USER_SOUNDS 1961 } else if (match_varname(buf, "SOUNDDIR", 8)) { 1962 sounddir = (char *)strdup(bufp); 1963 } else if (match_varname(buf, "SOUND", 5)) { 1964 add_sound_mapping(bufp); 1965#endif 1966#ifdef QT_GRAPHICS 1967 /* These should move to wc_ options */ 1968 } else if (match_varname(buf, "QT_TILEWIDTH", 12)) { 1969 extern char *qt_tilewidth; 1970 if (qt_tilewidth == NULL) 1971 qt_tilewidth=(char *)strdup(bufp); 1972 } else if (match_varname(buf, "QT_TILEHEIGHT", 13)) { 1973 extern char *qt_tileheight; 1974 if (qt_tileheight == NULL) 1975 qt_tileheight=(char *)strdup(bufp); 1976 } else if (match_varname(buf, "QT_FONTSIZE", 11)) { 1977 extern char *qt_fontsize; 1978 if (qt_fontsize == NULL) 1979 qt_fontsize=(char *)strdup(bufp); 1980 } else if (match_varname(buf, "QT_COMPACT", 10)) { 1981 extern int qt_compact_mode; 1982 qt_compact_mode = atoi(bufp); 1983#endif 1984 } else 1985 return 0; 1986 return 1; 1987} 1988 1989#ifdef USER_SOUNDS 1990boolean 1991can_read_file(filename) 1992const char *filename; 1993{ 1994 return (access(filename, 4) == 0); 1995} 1996#endif /* USER_SOUNDS */ 1997 1998void 1999read_config_file(filename) 2000const char *filename; 2001{ 2002#define tmp_levels (char *)0 2003#define tmp_ramdisk (char *)0 2004 2005#if defined(MICRO) || defined(WIN32) 2006#undef tmp_levels 2007 char tmp_levels[PATHLEN]; 2008# ifdef MFLOPPY 2009# ifndef AMIGA 2010#undef tmp_ramdisk 2011 char tmp_ramdisk[PATHLEN]; 2012# endif 2013# endif 2014#endif 2015 char buf[4*BUFSZ]; 2016 FILE *fp; 2017 2018 if (!(fp = fopen_config_file(filename))) return; 2019 2020#if defined(MICRO) || defined(WIN32) 2021# ifdef MFLOPPY 2022# ifndef AMIGA 2023 tmp_ramdisk[0] = 0; 2024# endif 2025# endif 2026 tmp_levels[0] = 0; 2027#endif 2028 /* begin detection of duplicate configfile options */ 2029 set_duplicate_opt_detection(1); 2030 2031 while (fgets(buf, 4*BUFSZ, fp)) { 2032 if (!parse_config_line(fp, buf, tmp_ramdisk, tmp_levels)) { 2033 raw_printf("Bad option line: \"%.50s\"", buf); 2034 wait_synch(); 2035 } 2036 } 2037 (void) fclose(fp); 2038 2039 /* turn off detection of duplicate configfile options */ 2040 set_duplicate_opt_detection(0); 2041 2042#if defined(MICRO) && !defined(NOCWD_ASSUMPTIONS) 2043 /* should be superseded by fqn_prefix[] */ 2044# ifdef MFLOPPY 2045 Strcpy(permbones, tmp_levels); 2046# ifndef AMIGA 2047 if (tmp_ramdisk[0]) { 2048 Strcpy(levels, tmp_ramdisk); 2049 if (strcmp(permbones, levels)) /* if not identical */ 2050 ramdisk = TRUE; 2051 } else 2052# endif /* AMIGA */ 2053 Strcpy(levels, tmp_levels); 2054 2055 Strcpy(bones, levels); 2056# endif /* MFLOPPY */ 2057#endif /* MICRO */ 2058 return; 2059} 2060 2061#ifdef WIZARD 2062STATIC_OVL FILE * 2063fopen_wizkit_file() 2064{ 2065 FILE *fp; 2066#if defined(VMS) || defined(UNIX) 2067 char tmp_wizkit[BUFSZ]; 2068#endif 2069 char *envp; 2070 2071 envp = nh_getenv("WIZKIT"); 2072 if (envp && *envp) (void) strncpy(wizkit, envp, WIZKIT_MAX - 1); 2073 if (!wizkit[0]) return (FILE *)0; 2074 2075#ifdef UNIX 2076 if (access(wizkit, 4) == -1) { 2077 /* 4 is R_OK on newer systems */ 2078 /* nasty sneaky attempt to read file through 2079 * NetHack's setuid permissions -- this is a 2080 * place a file name may be wholly under the player's 2081 * control 2082 */ 2083 raw_printf("Access to %s denied (%d).", 2084 wizkit, errno); 2085 wait_synch(); 2086 /* fall through to standard names */ 2087 } else 2088#endif 2089 if ((fp = fopenp(wizkit, "r")) != (FILE *)0) { 2090 return(fp); 2091#if defined(UNIX) || defined(VMS) 2092 } else { 2093 /* access() above probably caught most problems for UNIX */ 2094 raw_printf("Couldn't open requested config file %s (%d).", 2095 wizkit, errno); 2096 wait_synch(); 2097#endif 2098 } 2099 2100#if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32) 2101 if ((fp = fopenp(fqname(wizkit, CONFIGPREFIX, 0), "r")) 2102 != (FILE *)0) 2103 return(fp); 2104#else 2105# ifdef VMS 2106 envp = nh_getenv("HOME"); 2107 if (envp) 2108 Sprintf(tmp_wizkit, "%s%s", envp, wizkit); 2109 else 2110 Sprintf(tmp_wizkit, "%s%s", "sys$login:", wizkit); 2111 if ((fp = fopenp(tmp_wizkit, "r")) != (FILE *)0) 2112 return(fp); 2113# else /* should be only UNIX left */ 2114 envp = nh_getenv("HOME"); 2115 if (envp) 2116 Sprintf(tmp_wizkit, "%s/%s", envp, wizkit); 2117 else Strcpy(tmp_wizkit, wizkit); 2118 if ((fp = fopenp(tmp_wizkit, "r")) != (FILE *)0) 2119 return(fp); 2120 else if (errno != ENOENT) { 2121 /* e.g., problems when setuid NetHack can't search home 2122 * directory restricted to user */ 2123 raw_printf("Couldn't open default wizkit file %s (%d).", 2124 tmp_wizkit, errno); 2125 wait_synch(); 2126 } 2127# endif 2128#endif 2129 return (FILE *)0; 2130} 2131 2132void 2133read_wizkit() 2134{ 2135 FILE *fp; 2136 char *ep, buf[BUFSZ]; 2137 struct obj *otmp; 2138 boolean bad_items = FALSE, skip = FALSE; 2139 2140 if (!wizard || !(fp = fopen_wizkit_file())) return; 2141 2142 while (fgets(buf, (int)(sizeof buf), fp)) { 2143 ep = index(buf, '\n'); 2144 if (skip) { /* in case previous line was too long */ 2145 if (ep) skip = FALSE; /* found newline; next line is normal */ 2146 } else { 2147 if (!ep) skip = TRUE; /* newline missing; discard next fgets */ 2148 else *ep = '\0'; /* remove newline */ 2149 2150 if (buf[0]) { 2151 otmp = readobjnam(buf, (struct obj *)0, FALSE); 2152 if (otmp) { 2153 if (otmp != &zeroobj) 2154 otmp = addinv(otmp); 2155 } else { 2156 /* .60 limits output line width to 79 chars */ 2157 raw_printf("Bad wizkit item: \"%.60s\"", buf); 2158 bad_items = TRUE; 2159 } 2160 } 2161 } 2162 } 2163 if (bad_items) 2164 wait_synch(); 2165 (void) fclose(fp); 2166 return; 2167} 2168 2169#endif /*WIZARD*/ 2170 2171/* ---------- END CONFIG FILE HANDLING ----------- */ 2172 2173/* ---------- BEGIN SCOREBOARD CREATION ----------- */ 2174 2175/* verify that we can write to the scoreboard file; if not, try to create one */ 2176void 2177check_recordfile(dir) 2178const char *dir; 2179{ 2180#if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__) 2181# pragma unused(dir) 2182#endif 2183 const char *fq_record; 2184 int fd; 2185 2186#if defined(UNIX) || defined(VMS) 2187 fq_record = fqname(RECORD, SCOREPREFIX, 0); 2188 fd = open(fq_record, O_RDWR, 0); 2189 if (fd >= 0) { 2190# ifdef VMS /* must be stream-lf to use UPDATE_RECORD_IN_PLACE */ 2191 if (!file_is_stmlf(fd)) { 2192 raw_printf( 2193 "Warning: scoreboard file %s is not in stream_lf format", 2194 fq_record); 2195 wait_synch(); 2196 } 2197# endif 2198 (void) close(fd); /* RECORD is accessible */ 2199 } else if ((fd = open(fq_record, O_CREAT|O_RDWR, FCMASK)) >= 0) { 2200 (void) close(fd); /* RECORD newly created */ 2201# if defined(VMS) && !defined(SECURE) 2202 /* Re-protect RECORD with world:read+write+execute+delete access. */ 2203 (void) chmod(fq_record, FCMASK | 007); 2204# endif /* VMS && !SECURE */ 2205 } else { 2206 raw_printf("Warning: cannot write scoreboard file %s", fq_record); 2207 wait_synch(); 2208 } 2209#endif /* !UNIX && !VMS */ 2210#if defined(MICRO) || defined(WIN32) 2211 char tmp[PATHLEN]; 2212 2213# ifdef OS2_CODEVIEW /* explicit path on opening for OS/2 */ 2214 /* how does this work when there isn't an explicit path or fopenp 2215 * for later access to the file via fopen_datafile? ? */ 2216 (void) strncpy(tmp, dir, PATHLEN - 1); 2217 tmp[PATHLEN-1] = '\0'; 2218 if ((strlen(tmp) + 1 + strlen(RECORD)) < (PATHLEN - 1)) { 2219 append_slash(tmp); 2220 Strcat(tmp, RECORD); 2221 } 2222 fq_record = tmp; 2223# else 2224 Strcpy(tmp, RECORD); 2225 fq_record = fqname(RECORD, SCOREPREFIX, 0); 2226# endif 2227 2228 if ((fd = open(fq_record, O_RDWR)) < 0) { 2229 /* try to create empty record */ 2230# if defined(AZTEC_C) || defined(_DCC) || (defined(__GNUC__) && defined(__AMIGA__)) 2231 /* Aztec doesn't use the third argument */ 2232 /* DICE doesn't like it */ 2233 if ((fd = open(fq_record, O_CREAT|O_RDWR)) < 0) { 2234# else 2235 if ((fd = open(fq_record, O_CREAT|O_RDWR, S_IREAD|S_IWRITE)) < 0) { 2236# endif 2237 raw_printf("Warning: cannot write record %s", tmp); 2238 wait_synch(); 2239 } else 2240 (void) close(fd); 2241 } else /* open succeeded */ 2242 (void) close(fd); 2243#else /* MICRO || WIN32*/ 2244 2245# ifdef MAC 2246 /* Create the "record" file, if necessary */ 2247 fq_record = fqname(RECORD, SCOREPREFIX, 0); 2248 fd = macopen (fq_record, O_RDWR | O_CREAT, TEXT_TYPE); 2249 if (fd != -1) macclose (fd); 2250# endif /* MAC */ 2251 2252#endif /* MICRO || WIN32*/ 2253} 2254 2255/* ---------- END SCOREBOARD CREATION ----------- */ 2256 2257/* ---------- BEGIN PANIC/IMPOSSIBLE LOG ----------- */ 2258 2259/*ARGSUSED*/ 2260void 2261paniclog(type, reason) 2262const char *type; /* panic, impossible, trickery */ 2263const char *reason; /* explanation */ 2264{ 2265#ifdef PANICLOG 2266 FILE *lfile; 2267 char buf[BUFSZ]; 2268 2269 if (!program_state.in_paniclog) { 2270 program_state.in_paniclog = 1; 2271 lfile = fopen_datafile(PANICLOG, "a", TROUBLEPREFIX); 2272 if (lfile) { 2273 (void) fprintf(lfile, "%s %08ld: %s %s\n", 2274 version_string(buf), yyyymmdd((time_t)0L), 2275 type, reason); 2276 (void) fclose(lfile); 2277 } 2278 program_state.in_paniclog = 0; 2279 } 2280#endif /* PANICLOG */ 2281 return; 2282} 2283 2284/* ---------- END PANIC/IMPOSSIBLE LOG ----------- */ 2285 2286#ifdef SELF_RECOVER 2287 2288/* ---------- BEGIN INTERNAL RECOVER ----------- */ 2289boolean 2290recover_savefile() 2291{ 2292 int gfd, lfd, sfd; 2293 int lev, savelev, hpid; 2294 xchar levc; 2295 struct version_info version_data; 2296 int processed[256]; 2297 char savename[SAVESIZE], errbuf[BUFSZ]; 2298 2299 for (lev = 0; lev < 256; lev++) 2300 processed[lev] = 0; 2301 2302 /* level 0 file contains: 2303 * pid of creating process (ignored here) 2304 * level number for current level of save file 2305 * name of save file nethack would have created 2306 * and game state 2307 */ 2308 gfd = open_levelfile(0, errbuf); 2309 if (gfd < 0) { 2310 raw_printf("%s\n", errbuf); 2311 return FALSE; 2312 } 2313 if (read(gfd, (genericptr_t) &hpid, sizeof hpid) != sizeof hpid) { 2314 raw_printf( 2315"\nCheckpoint data incompletely written or subsequently clobbered. Recovery impossible."); 2316 (void)close(gfd); 2317 return FALSE; 2318 } 2319 if (read(gfd, (genericptr_t) &savelev, sizeof(savelev)) 2320 != sizeof(savelev)) { 2321 raw_printf("\nCheckpointing was not in effect for %s -- recovery impossible.\n", 2322 lock); 2323 (void)close(gfd); 2324 return FALSE; 2325 } 2326 if ((read(gfd, (genericptr_t) savename, sizeof savename) 2327 != sizeof savename) || 2328 (read(gfd, (genericptr_t) &version_data, sizeof version_data) 2329 != sizeof version_data)) { 2330 raw_printf("\nError reading %s -- can't recover.\n", lock); 2331 (void)close(gfd); 2332 return FALSE; 2333 } 2334 2335 /* save file should contain: 2336 * version info 2337 * current level (including pets) 2338 * (non-level-based) game state 2339 * other levels 2340 */ 2341 set_savefile_name(); 2342 sfd = create_savefile(); 2343 if (sfd < 0) { 2344 raw_printf("\nCannot recover savefile %s.\n", SAVEF); 2345 (void)close(gfd); 2346 return FALSE; 2347 } 2348 2349 lfd = open_levelfile(savelev, errbuf); 2350 if (lfd < 0) { 2351 raw_printf("\n%s\n", errbuf); 2352 (void)close(gfd); 2353 (void)close(sfd); 2354 delete_savefile(); 2355 return FALSE; 2356 } 2357 2358 if (write(sfd, (genericptr_t) &version_data, sizeof version_data) 2359 != sizeof version_data) { 2360 raw_printf("\nError writing %s; recovery failed.", SAVEF); 2361 (void)close(gfd); 2362 (void)close(sfd); 2363 delete_savefile(); 2364 return FALSE; 2365 } 2366 2367 if (!copy_bytes(lfd, sfd)) { 2368 (void) close(lfd); 2369 (void) close(sfd); 2370 delete_savefile(); 2371 return FALSE; 2372 } 2373 (void)close(lfd); 2374 processed[savelev] = 1; 2375 2376 if (!copy_bytes(gfd, sfd)) { 2377 (void) close(lfd); 2378 (void) close(sfd); 2379 delete_savefile(); 2380 return FALSE; 2381 } 2382 (void)close(gfd); 2383 processed[0] = 1; 2384 2385 for (lev = 1; lev < 256; lev++) { 2386 /* level numbers are kept in xchars in save.c, so the 2387 * maximum level number (for the endlevel) must be < 256 2388 */ 2389 if (lev != savelev) { 2390 lfd = open_levelfile(lev, (char *)0); 2391 if (lfd >= 0) { 2392 /* any or all of these may not exist */ 2393 levc = (xchar) lev; 2394 write(sfd, (genericptr_t) &levc, sizeof(levc)); 2395 if (!copy_bytes(lfd, sfd)) { 2396 (void) close(lfd); 2397 (void) close(sfd); 2398 delete_savefile(); 2399 return FALSE; 2400 } 2401 (void)close(lfd); 2402 processed[lev] = 1; 2403 } 2404 } 2405 } 2406 (void)close(sfd); 2407 2408#ifdef HOLD_LOCKFILE_OPEN 2409 really_close(); 2410#endif 2411 /* 2412 * We have a successful savefile! 2413 * Only now do we erase the level files. 2414 */ 2415 for (lev = 0; lev < 256; lev++) { 2416 if (processed[lev]) { 2417 const char *fq_lock; 2418 set_levelfile_name(lock, lev); 2419 fq_lock = fqname(lock, LEVELPREFIX, 3); 2420 (void) unlink(fq_lock); 2421 } 2422 } 2423 return TRUE; 2424} 2425 2426boolean 2427copy_bytes(ifd, ofd) 2428int ifd, ofd; 2429{ 2430 char buf[BUFSIZ]; 2431 int nfrom, nto; 2432 2433 do { 2434 nfrom = read(ifd, buf, BUFSIZ); 2435 nto = write(ofd, buf, nfrom); 2436 if (nto != nfrom) return FALSE; 2437 } while (nfrom == BUFSIZ); 2438 return TRUE; 2439} 2440 2441/* ---------- END INTERNAL RECOVER ----------- */ 2442#endif /*SELF_RECOVER*/ 2443 2444/*files.c*/ 2445