X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=src%2Ffiles.c;h=8ee8cff882fec182ee019feefb105c07d4ae8071;hb=294675b381ab5f81d50c4428f037c38986736202;hp=1b74a56b4dd2e4c7904bd06a8603f325433f82cf;hpb=a4c1eb849dac77e75f386da143ca16b6594ee544;p=jnethack%2Fsource.git diff --git a/src/files.c b/src/files.c index 1b74a56..8ee8cff 100644 --- a/src/files.c +++ b/src/files.c @@ -1,7 +1,15 @@ -/* NetHack 3.6 files.c $NHDT-Date: 1449296293 2015/12/05 06:18:13 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.192 $ */ +/* NetHack 3.6 files.c $NHDT-Date: 1524413723 2018/04/22 16:15:23 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.235 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ +#define NEED_VARARGS + +/* JNetHack Copyright */ +/* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000 */ +/* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2016 */ +/* JNetHack may be freely redistributed. See license for details. */ + #include "hack.h" #include "dlb.h" @@ -188,15 +196,24 @@ STATIC_DCL boolean FDECL(make_compressed_name, (const char *, char *)); #ifndef USE_FCNTL STATIC_DCL char *FDECL(make_lockname, (const char *, char *)); #endif +STATIC_DCL void FDECL(set_configfile_name, (const char *)); STATIC_DCL FILE *FDECL(fopen_config_file, (const char *, int)); -STATIC_DCL int FDECL(get_uchars, (FILE *, char *, char *, uchar *, BOOLEAN_P, +STATIC_DCL int FDECL(get_uchars, (char *, uchar *, BOOLEAN_P, int, const char *)); -int FDECL(parse_config_line, (FILE *, char *, int)); +boolean FDECL(proc_wizkit_line, (char *)); +boolean FDECL(parse_config_line, (char *)); +STATIC_DCL boolean FDECL(parse_conf_file, (FILE *, boolean (*proc)(char *))); STATIC_DCL FILE *NDECL(fopen_sym_file); +boolean FDECL(proc_symset_line, (char *)); STATIC_DCL void FDECL(set_symhandling, (char *, int)); #ifdef NOCWD_ASSUMPTIONS STATIC_DCL void FDECL(adjust_prefix, (char *, int)); #endif +STATIC_DCL boolean FDECL(config_error_nextline, (const char *)); +STATIC_DCL void NDECL(free_config_sections); +STATIC_DCL char *FDECL(choose_random_part, (char *, CHAR_P)); +STATIC_DCL boolean FDECL(is_config_section, (const char *)); +STATIC_DCL boolean FDECL(handle_config_section, (char *)); #ifdef SELF_RECOVER STATIC_DCL boolean FDECL(copy_bytes, (int, int)); #endif @@ -204,6 +221,10 @@ STATIC_DCL boolean FDECL(copy_bytes, (int, int)); STATIC_DCL int FDECL(open_levelfile_exclusively, (const char *, int, int)); #endif + +static char *config_section_chosen = (char *) 0; +static char *config_section_current = (char *) 0; + /* * fname_encode() * @@ -255,6 +276,13 @@ int bufsz; *op++ = *sp; *op = '\0'; cnt++; +#if 1 /*JP*/ + } else if (is_kanji1(s, sp - s)) { + *op++ = *sp++; + *op++ = *sp; + *op = '\0'; + cnt += 2; +#endif } else { (void) sprintf(op, "%c%02X", quotechar, *sp); op += 3; @@ -541,8 +569,13 @@ char errbuf[]; settle for `lock' instead of `fq_lock' because the latter might end up being too big for nethack's BUFSZ */ if (fd < 0 && errbuf) +#if 0 /*JP*/ Sprintf(errbuf, "Cannot open file \"%s\" for level %d (errno %d).", lock, lev, errno); +#else + Sprintf(errbuf, "’n‰º%dŠK‚̃tƒ@ƒCƒ‹\"%s\"‚ðŠJ‚¯‚È‚¢(errno %d)D", + lev, lock, errno); +#endif return fd; } @@ -616,6 +649,7 @@ const char *name; int lev, oflag; { int reslt, fd; + if (!lftrack.init) { lftrack.init = 1; lftrack.fd = -1; @@ -648,13 +682,17 @@ int lev, oflag; void really_close() { - int fd = lftrack.fd; + int fd; + + if (lftrack.init) { + fd = lftrack.fd; - lftrack.nethack_thinks_it_is_open = FALSE; - lftrack.fd = -1; - lftrack.oflag = 0; - if (fd != -1) - (void) close(fd); + lftrack.nethack_thinks_it_is_open = FALSE; + lftrack.fd = -1; + lftrack.oflag = 0; + if (fd != -1) + (void) close(fd); + } return; } @@ -670,7 +708,7 @@ int fd; } return close(fd); } -#else +#else /* !HOLD_LOCKFILE_OPEN */ int nhclose(fd) @@ -678,7 +716,7 @@ int fd; { return close(fd); } -#endif +#endif /* ?HOLD_LOCKFILE_OPEN */ /* ---------- END LEVEL FILE HANDLING ----------- */ @@ -695,17 +733,47 @@ d_level *lev; s_level *sptr; char *dptr; - Sprintf(file, "bon%c%s", dungeons[lev->dnum].boneid, + /* + * "bonD0.nn" = bones for level nn in the main dungeon; + * "bonM0.T" = bones for Minetown; + * "bonQBar.n" = bones for level n in the Barbarian quest; + * "bon3D0.nn" = \ + * "bon3M0.T" = > same as above, but for bones pool #3. + * "bon3QBar.n" = / + * + * Return value for content validation skips "bon" and the + * pool number (if present), making it feasible for the admin + * to manually move a bones file from one pool to another by + * renaming it. + */ + Strcpy(file, "bon"); +#ifdef SYSCF + if (sysopt.bones_pools > 1) { + unsigned poolnum = min((unsigned) sysopt.bones_pools, 10); + + poolnum = (unsigned) ubirthday % poolnum; /* 0..9 */ + Sprintf(eos(file), "%u", poolnum); + } +#endif + dptr = eos(file); /* this used to be after the following Sprintf() + and the return value was (dptr - 2) */ + /* when this naming scheme was adopted, 'filecode' was one letter; + 3.3.0 turned it into a three letter string (via roles[] in role.c); + from that version through 3.6.0, 'dptr' pointed past the filecode + and the return value of (dptr - 2) was wrong for bones produced + in the quest branch, skipping the boneid character 'Q' and the + first letter of the role's filecode; bones loading still worked + because the bonesid used for validation had the same error */ + Sprintf(dptr, "%c%s", dungeons[lev->dnum].boneid, In_quest(lev) ? urole.filecode : "0"); - dptr = eos(file); if ((sptr = Is_special(lev)) != 0) - Sprintf(dptr, ".%c", sptr->boneid); + Sprintf(eos(dptr), ".%c", sptr->boneid); else - Sprintf(dptr, ".%d", lev->dlevel); + Sprintf(eos(dptr), ".%d", lev->dlevel); #ifdef VMS Strcat(dptr, ";1"); #endif - return (dptr - 2); + return dptr; } /* set up temporary file name for writing bones, to avoid another game's @@ -913,7 +981,10 @@ void save_savefile_name(fd) int fd; { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-result" (void) write(fd, (genericptr_t) SAVEF, sizeof(SAVEF)); +#pragma GCC diagnostic pop } #endif @@ -1203,7 +1274,7 @@ boolean uncomp; if (freopen(filename, mode, stream) == (FILE *) 0) { (void) fprintf(stderr, "freopen of %s for %scompress failed\n", filename, uncomp ? "un" : ""); - terminate(EXIT_FAILURE); + nh_terminate(EXIT_FAILURE); } } @@ -1303,13 +1374,16 @@ boolean uncomp; redirect(filename, RDBMODE, stdin, uncomp); redirect(cfn, WRBMODE, stdout, uncomp); } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-result" (void) setgid(getgid()); (void) setuid(getuid()); +#pragma GCC diagnostic pop (void) execv(args[0], (char *const *) args); perror((char *) 0); (void) fprintf(stderr, "Exec to %scompress %s failed.\n", uncomp ? "un" : "", filename); - terminate(EXIT_FAILURE); + nh_terminate(EXIT_FAILURE); } else if (f == -1) { perror((char *) 0); pline("Fork to %scompress %s failed.", uncomp ? "un" : "", filename); @@ -1641,8 +1715,10 @@ int retryct; #ifdef USE_FCNTL lockfd = open(filename, O_RDWR); if (lockfd == -1) { - HUP raw_printf("Cannot open file %s. This is a program bug.", + HUP raw_printf("Cannot open file %s. Is NetHack installed correctly?", filename); + nesting--; + return FALSE; } sflock.l_type = F_WRLCK; sflock.l_whence = SEEK_SET; @@ -1821,7 +1897,7 @@ const char *filename; /* ---------- BEGIN CONFIG FILE HANDLING ----------- */ -const char *configfile = +const char *default_configfile = #ifdef UNIX ".nethackrc"; #else @@ -1837,7 +1913,7 @@ const char *configfile = #endif /* used for messaging */ -char lastconfigfile[BUFSZ]; +char configfile[BUFSZ]; #ifdef MSDOS /* conflict with speed-dial under windows @@ -1850,6 +1926,16 @@ char lastconfigfile[BUFSZ]; const char *backward_compat_configfile = "nethack.cnf"; #endif +/* remember the name of the file we're accessing; + if may be used in option reject messages */ +STATIC_OVL void +set_configfile_name(fname) +const char *fname; +{ + (void) strncpy(configfile, fname, sizeof configfile - 1); + configfile[sizeof configfile - 1] = '\0'; +} + #ifndef MFLOPPY #define fopenp fopen #endif @@ -1865,142 +1951,150 @@ int src; char *envp; #endif + if (src == SET_IN_SYS) { + /* SYSCF_FILE; if we can't open it, caller will bail */ + if (filename && *filename) { + set_configfile_name(fqname(filename, SYSCONFPREFIX, 0)); + fp = fopenp(configfile, "r"); + } else + fp = (FILE *) 0; + return fp; + } /* If src != SET_IN_SYS, "filename" is an environment variable, so it * should hang around. If set, it is expected to be a full path name - * (if relevant) */ - if (filename) { + * (if relevant) + */ + if (filename && *filename) { + set_configfile_name(filename); #ifdef UNIX - if ((src != SET_IN_SYS) && access(filename, 4) == -1) { - /* 4 is R_OK on newer systems */ + if (access(configfile, 4) == -1) { /* 4 is R_OK on newer systems */ /* nasty sneaky attempt to read file through * NetHack's setuid permissions -- this is the only * place a file name may be wholly under the player's * control (but SYSCF_FILE is not under the player's * control so it's OK). */ - raw_printf("Access to %s denied (%d).", filename, errno); + raw_printf("Access to %s denied (%d).", configfile, errno); wait_synch(); /* fall through to standard names */ } else #endif -#ifdef PREFIXES_IN_USE - if (src == SET_IN_SYS) { - (void) strncpy(lastconfigfile, fqname(filename, SYSCONFPREFIX, 0), - BUFSZ - 1); - } else -#endif - /* always honor sysconf first before anything else */ - (void) strncpy(lastconfigfile, filename, BUFSZ - 1); - lastconfigfile[BUFSZ - 1] = '\0'; - if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0) - return fp; - if ((fp = fopenp(filename, "r")) != (FILE *) 0) { + if ((fp = fopenp(configfile, "r")) != (FILE *) 0) { return fp; #if defined(UNIX) || defined(VMS) } else { /* access() above probably caught most problems for UNIX */ raw_printf("Couldn't open requested config file %s (%d).", - filename, errno); + configfile, errno); wait_synch(); -/* fall through to standard names */ #endif } } + /* fall through to standard names */ #if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32) - if ((fp = fopenp(fqname(configfile, CONFIGPREFIX, 0), "r")) != (FILE *) 0) - return fp; - if ((fp = fopenp(configfile, "r")) != (FILE *) 0) + set_configfile_name(fqname(default_configfile, CONFIGPREFIX, 0)); + if ((fp = fopenp(configfile, "r")) != (FILE *) 0) { return fp; + } else if (strcmp(default_configfile, configfile)) { + set_configfile_name(default_configfile); + if ((fp = fopenp(configfile, "r")) != (FILE *) 0) + return fp; + } #ifdef MSDOS - if ((fp = fopenp(fqname(backward_compat_configfile, CONFIGPREFIX, 0), - "r")) != (FILE *) 0) - return fp; - if ((fp = fopenp(backward_compat_configfile, "r")) != (FILE *) 0) + set_configfile_name(fqname(backward_compat_configfile, CONFIGPREFIX, 0)); + if ((fp = fopenp(configfile, "r")) != (FILE *) 0) { return fp; + } else if (strcmp(backward_compat_configfile, configfile)) { + set_configfile_name(backward_compat_configfile); + if ((fp = fopenp(configfile, "r")) != (FILE *) 0) + return fp; + } #endif #else /* constructed full path names don't need fqname() */ #ifdef VMS - (void) strncpy(lastconfigfile, fqname("nethackini", CONFIGPREFIX, 0), - BUFSZ - 1); - lastconfigfile[BUFSZ - 1] = '\0'; - if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0) { + /* no punctuation, so might be a logical name */ + set_configfile_name(fqname("nethackini", CONFIGPREFIX, 0)); + if ((fp = fopenp(configfile, "r")) != (FILE *) 0) return fp; - } - (void) strncpy(lastconfigfile, "sys$login:nethack.ini", BUFSZ - 1); - lastconfigfile[BUFSZ - 1] = '\0'; - if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0) { + set_configfile_name("sys$login:nethack.ini"); + if ((fp = fopenp(configfile, "r")) != (FILE *) 0) return fp; - } envp = nh_getenv("HOME"); - if (!envp) + if (!envp || !*envp) Strcpy(tmp_config, "NetHack.cnf"); else - Sprintf(tmp_config, "%s%s", envp, "NetHack.cnf"); - - (void) strncpy(lastconfigfile, tmp_config, BUFSZ - 1); - lastconfigfile[BUFSZ - 1] = '\0'; - if ((fp = fopenp(tmp_config, "r")) != (FILE *) 0) + Sprintf(tmp_config, "%s%s%s", envp, + !index(":]>/", envp[strlen(envp) - 1]) ? "/" : "", + "NetHack.cnf"); + set_configfile_name(tmp_config); + if ((fp = fopenp(configfile, "r")) != (FILE *) 0) return fp; #else /* should be only UNIX left */ envp = nh_getenv("HOME"); +#if 1 /*JP*//*".jnethackrc"‚ð—Dæ‚µ‚ēǂݍž‚Ý*/ + if (!envp) + Strcpy(tmp_config, ".jnethackrc"); + else + Sprintf(tmp_config, "%s/%s", envp, ".jnethackrc"); + + set_configfile_name(tmp_config); + if ((fp = fopenp(configfile, "r")) != (FILE *) 0) + return fp; +#endif if (!envp) Strcpy(tmp_config, ".nethackrc"); else Sprintf(tmp_config, "%s/%s", envp, ".nethackrc"); - (void) strncpy(lastconfigfile, tmp_config, BUFSZ - 1); - lastconfigfile[BUFSZ - 1] = '\0'; - if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0) + set_configfile_name(tmp_config); + if ((fp = fopenp(configfile, "r")) != (FILE *) 0) return fp; -#if defined(__APPLE__) +#if defined(__APPLE__) /* UNIX+__APPLE__ => MacOSX */ /* try an alternative */ if (envp) { + /* OSX-style configuration settings */ Sprintf(tmp_config, "%s/%s", envp, "Library/Preferences/NetHack Defaults"); - (void) strncpy(lastconfigfile, tmp_config, BUFSZ - 1); - lastconfigfile[BUFSZ - 1] = '\0'; - if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0) + set_configfile_name(tmp_config); + if ((fp = fopenp(configfile, "r")) != (FILE *) 0) return fp; + /* may be easier for user to edit if filename as '.txt' suffix */ Sprintf(tmp_config, "%s/%s", envp, "Library/Preferences/NetHack Defaults.txt"); - (void) strncpy(lastconfigfile, tmp_config, BUFSZ - 1); - lastconfigfile[BUFSZ - 1] = '\0'; - if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0) + set_configfile_name(tmp_config); + if ((fp = fopenp(configfile, "r")) != (FILE *) 0) return fp; } -#endif +#endif /*__APPLE__*/ if (errno != ENOENT) { const char *details; -/* e.g., problems when setuid NetHack can't search home - * directory restricted to user */ - + /* e.g., problems when setuid NetHack can't search home + directory restricted to user */ #if defined(NHSTDC) && !defined(NOTSTDC) if ((details = strerror(errno)) == 0) #endif details = ""; raw_printf("Couldn't open default config file %s %s(%d).", - lastconfigfile, details, errno); + configfile, details, errno); wait_synch(); } -#endif /* Unix */ -#endif +#endif /* !VMS => Unix */ +#endif /* !(MICRO || MAC || __BEOS__ || WIN32) */ return (FILE *) 0; } /* - * Retrieve a list of integers from a file into a uchar array. + * Retrieve a list of integers from buf into a uchar array. * * NOTE: zeros are inserted unless modlist is TRUE, in which case the list * location is unchanged. Callers must handle zeros if modlist is FALSE. */ STATIC_OVL int -get_uchars(fp, buf, bufp, list, modlist, size, name) -FILE *fp; /* input file pointer */ -char *buf; /* read buffer, must be of size BUFSZ */ +get_uchars(bufp, list, modlist, size, name) char *bufp; /* current pointer */ uchar *list; /* return list */ boolean modlist; /* TRUE: list is being modified in place */ @@ -2046,13 +2140,7 @@ const char *name; /* name of option for error message */ break; case '\\': - if (fp == (FILE *) 0) - goto gi_error; - do { - if (!fgets(buf, BUFSZ, fp)) - goto gi_error; - } while (buf[0] == '#'); - bufp = buf; + goto gi_error; break; default: @@ -2086,13 +2174,111 @@ int prefixid; } #endif +/* Choose at random one of the sep separated parts from str. Mangles str. */ +STATIC_OVL char * +choose_random_part(str,sep) +char *str; +char sep; +{ + int nsep = 1; + int csep; + int len = 0; + char *begin = str; + + if (!str) + return (char *) 0; + + while (*str) { + if (*str == sep) nsep++; + str++; + } + csep = rn2(nsep); + str = begin; + while ((csep > 0) && *str) { + str++; + if (*str == sep) csep--; + } + if (*str) { + if (*str == sep) str++; + begin = str; + while (*str && *str != sep) { + str++; + len++; + } + *str = '\0'; + if (len) + return begin; + } + return (char *) 0; +} + +STATIC_OVL void +free_config_sections() +{ + if (config_section_chosen) { + free(config_section_chosen); + config_section_chosen = NULL; + } + if (config_section_current) { + free(config_section_current); + config_section_current = NULL; + } +} + +STATIC_OVL boolean +is_config_section(str) +const char *str; +{ + const char *a = rindex(str, ']'); + + return (a && *str == '[' && *(a+1) == '\0' && (int)(a - str) > 0); +} + +STATIC_OVL boolean +handle_config_section(buf) +char *buf; +{ + if (is_config_section(buf)) { + char *send; + if (config_section_current) { + free(config_section_current); + } + config_section_current = dupstr(&buf[1]); + send = rindex(config_section_current, ']'); + *send = '\0'; + debugpline1("set config section: '%s'", config_section_current); + return TRUE; + } + + if (config_section_current) { + if (!config_section_chosen) + return TRUE; + if (strcmp(config_section_current, config_section_chosen)) + return TRUE; + } + return FALSE; +} + #define match_varname(INP, NAM, LEN) match_optname(INP, NAM, LEN, TRUE) -int -parse_config_line(fp, origbuf, src) -FILE *fp; +/* find the '=' or ':' */ +char * +find_optparam(buf) +const char *buf; +{ + char *bufp, *altp; + + bufp = index(buf, '='); + altp = index(buf, ':'); + if (!bufp || (altp && altp < bufp)) + bufp = altp; + + return bufp; +} + +boolean +parse_config_line(origbuf) char *origbuf; -int src; { #if defined(MICRO) && !defined(NOCWD_ASSUMPTIONS) static boolean ramdisk_specified = FALSE; @@ -2100,25 +2286,23 @@ int src; #ifdef SYSCF int n; #endif - char *bufp, *altp, buf[BUFSZ]; + char *bufp, buf[4 * BUFSZ]; uchar translate[MAXPCHARS]; int len; + boolean retval = TRUE; + int src = iflags.parse_config_file_src; /* convert any tab to space, condense consecutive spaces into one, remove leading and trailing spaces (exception: if there is nothing but spaces, one of them will be kept even though it leads/trails) */ mungspaces(strcpy(buf, origbuf)); - /* lines beginning with '#' are comments; accept empty lines too */ - if (!*buf || *buf == '#' || !strcmp(buf, " ")) - return 1; /* find the '=' or ':' */ - bufp = index(buf, '='); - altp = index(buf, ':'); - if (!bufp || (altp && altp < bufp)) - bufp = altp; - if (!bufp) - return 0; + bufp = find_optparam(buf); + if (!bufp) { + config_error_add("Not a config statement, missing '='"); + return FALSE; + } /* skip past '=', then space between it and value, if any */ ++bufp; if (*bufp == ' ') @@ -2132,19 +2316,21 @@ int src; /* hack: un-mungspaces to allow consecutive spaces in general options until we verify that this is unnecessary; '=' or ':' is guaranteed to be present */ - bufp = index(origbuf, '='); - altp = index(origbuf, ':'); - if (!bufp || (altp && altp < bufp)) - bufp = altp; + bufp = find_optparam(origbuf); ++bufp; /* skip '='; parseoptions() handles spaces */ - parseoptions(bufp, TRUE, TRUE); - if (plname[0]) /* If a name was given */ - plnamesuffix(); /* set the character class */ + if (!parseoptions(bufp, TRUE, TRUE)) + retval = FALSE; } else if (match_varname(buf, "AUTOPICKUP_EXCEPTION", 5)) { add_autopickup_exception(bufp); + } else if (match_varname(buf, "BINDINGS", 4)) { + if (!parsebindings(bufp)) + retval = FALSE; + } else if (match_varname(buf, "AUTOCOMPLETE", 5)) { + parseautocomplete(bufp, TRUE); } else if (match_varname(buf, "MSGTYPE", 7)) { - (void) msgtype_parse_add(bufp); + if (!msgtype_parse_add(bufp)) + retval = FALSE; #ifdef NOCWD_ASSUMPTIONS } else if (match_varname(buf, "HACKDIR", 4)) { adjust_prefix(bufp, HACKPREFIX); @@ -2215,7 +2401,6 @@ int src; } else if (match_varname(buf, "NAME", 4)) { (void) strncpy(plname, bufp, PL_NSIZ - 1); - plnamesuffix(); } else if (match_varname(buf, "ROLE", 4) || match_varname(buf, "CHARACTER", 4)) { if ((len = str2role(bufp)) >= 0) @@ -2255,6 +2440,24 @@ int src; free((genericptr_t) sysopt.debugfiles); sysopt.debugfiles = dupstr(bufp); } + } else if (src == SET_IN_SYS && match_varname(buf, "DUMPLOGFILE", 7)) { +#ifdef DUMPLOG + if (sysopt.dumplogfile) + free((genericptr_t) sysopt.dumplogfile); + sysopt.dumplogfile = dupstr(bufp); +#endif + } else if (src == SET_IN_SYS && match_varname(buf, "GENERICUSERS", 12)) { + if (sysopt.genericusers) + free((genericptr_t) sysopt.genericusers); + sysopt.genericusers = dupstr(bufp); + } else if (src == SET_IN_SYS && match_varname(buf, "BONES_POOLS", 10)) { + /* max value of 10 guarantees (N % bones.pools) will be one digit + so we don't lose control of the length of bones file names */ + n = atoi(bufp); + sysopt.bones_pools = (n <= 0) ? 0 : min(n, 10); + /* note: right now bones_pools==0 is the same as bones_pools==1, + but we could change that and make bones_pools==0 become an + indicator to suppress bones usage altogether */ } else if (src == SET_IN_SYS && match_varname(buf, "SUPPORT", 7)) { if (sysopt.support) free((genericptr_t) sysopt.support); @@ -2267,58 +2470,62 @@ int src; && match_varname(buf, "CHECK_SAVE_UID", 14)) { n = atoi(bufp); sysopt.check_save_uid = n; + } else if (src == SET_IN_SYS + && match_varname(buf, "CHECK_PLNAME", 12)) { + n = atoi(bufp); + sysopt.check_plname = n; } else if (match_varname(buf, "SEDUCE", 6)) { n = !!atoi(bufp); /* XXX this could be tighter */ /* allow anyone to turn it off, but only sysconf to turn it on*/ if (src != SET_IN_SYS && n != 0) { - raw_printf("Illegal value in SEDUCE"); - return 0; + config_error_add("Illegal value in SEDUCE"); + return FALSE; } sysopt.seduce = n; sysopt_seduce_set(sysopt.seduce); } else if (src == SET_IN_SYS && match_varname(buf, "MAXPLAYERS", 10)) { n = atoi(bufp); /* XXX to get more than 25, need to rewrite all lock code */ - if (n < 1 || n > 25) { - raw_printf("Illegal value in MAXPLAYERS (maximum is 25)."); - return 0; + if (n < 0 || n > 25) { + config_error_add("Illegal value in MAXPLAYERS (maximum is 25)."); + return FALSE; } sysopt.maxplayers = n; } else if (src == SET_IN_SYS && match_varname(buf, "PERSMAX", 7)) { n = atoi(bufp); if (n < 1) { - raw_printf("Illegal value in PERSMAX (minimum is 1)."); - return 0; + config_error_add("Illegal value in PERSMAX (minimum is 1)."); + return FALSE; } sysopt.persmax = n; } else if (src == SET_IN_SYS && match_varname(buf, "PERS_IS_UID", 11)) { n = atoi(bufp); if (n != 0 && n != 1) { - raw_printf("Illegal value in PERS_IS_UID (must be 0 or 1)."); - return 0; + config_error_add("Illegal value in PERS_IS_UID (must be 0 or 1)."); + return FALSE; } sysopt.pers_is_uid = n; } else if (src == SET_IN_SYS && match_varname(buf, "ENTRYMAX", 8)) { n = atoi(bufp); if (n < 10) { - raw_printf("Illegal value in ENTRYMAX (minimum is 10)."); - return 0; + config_error_add("Illegal value in ENTRYMAX (minimum is 10)."); + return FALSE; } sysopt.entrymax = n; } else if ((src == SET_IN_SYS) && match_varname(buf, "POINTSMIN", 9)) { n = atoi(bufp); if (n < 1) { - raw_printf("Illegal value in POINTSMIN (minimum is 1)."); - return 0; + config_error_add("Illegal value in POINTSMIN (minimum is 1)."); + return FALSE; } sysopt.pointsmin = n; } else if (src == SET_IN_SYS && match_varname(buf, "MAX_STATUENAME_RANK", 10)) { n = atoi(bufp); if (n < 1) { - raw_printf( + config_error_add( "Illegal value in MAX_STATUENAME_RANK (minimum is 1)."); - return 0; + return FALSE; } sysopt.tt_oname_maxrank = n; @@ -2328,8 +2535,8 @@ int src; n = atoi(bufp); #if defined(PANICTRACE) && defined(PANICTRACE_LIBC) if (n < 0 || n > 2) { - raw_printf("Illegal value in PANICTRACE_LIBC (not 0,1,2)."); - return 0; + config_error_add("Illegal value in PANICTRACE_LIBC (not 0,1,2)."); + return FALSE; } #endif sysopt.panictrace_libc = n; @@ -2338,16 +2545,16 @@ int src; n = atoi(bufp); #if defined(PANICTRACE) if (n < 0 || n > 2) { - raw_printf("Illegal value in PANICTRACE_GDB (not 0,1,2)."); - return 0; + config_error_add("Illegal value in PANICTRACE_GDB (not 0,1,2)."); + return FALSE; } #endif sysopt.panictrace_gdb = n; } else if (src == SET_IN_SYS && match_varname(buf, "GDBPATH", 7)) { #if defined(PANICTRACE) && !defined(VMS) if (!file_exists(bufp)) { - raw_printf("File specified in GDBPATH does not exist."); - return 0; + config_error_add("File specified in GDBPATH does not exist."); + return FALSE; } #endif if (sysopt.gdbpath) @@ -2356,8 +2563,8 @@ int src; } else if (src == SET_IN_SYS && match_varname(buf, "GREPPATH", 7)) { #if defined(PANICTRACE) && !defined(VMS) if (!file_exists(bufp)) { - raw_printf("File specified in GREPPATH does not exist."); - return 0; + config_error_add("File specified in GREPPATH does not exist."); + return FALSE; } #endif if (sysopt.greppath) @@ -2366,42 +2573,25 @@ int src; #endif /* SYSCF */ } else if (match_varname(buf, "BOULDER", 3)) { - (void) get_uchars(fp, buf, bufp, &iflags.bouldersym, TRUE, 1, + (void) get_uchars(bufp, &iflags.bouldersym, TRUE, 1, "BOULDER"); } else if (match_varname(buf, "MENUCOLOR", 9)) { - (void) add_menu_coloring(bufp); + if (!add_menu_coloring(bufp)) + retval = FALSE; + } else if (match_varname(buf, "HILITE_STATUS", 6)) { +#ifdef STATUS_HILITES + if (!parse_status_hl1(bufp, TRUE)) + retval = FALSE; +#endif } else if (match_varname(buf, "WARNINGS", 5)) { - (void) get_uchars(fp, buf, bufp, translate, FALSE, WARNCOUNT, + (void) get_uchars(bufp, translate, FALSE, WARNCOUNT, "WARNINGS"); assign_warnings(translate); } else if (match_varname(buf, "SYMBOLS", 4)) { - char *op, symbuf[BUFSZ]; - boolean morelines; - - do { - /* check for line continuation (trailing '\') */ - op = eos(bufp); - morelines = (--op >= bufp && *op == '\\'); - if (morelines) { - *op = '\0'; - /* strip trailing space now that '\' is gone */ - if (--op >= bufp && *op == ' ') - *op = '\0'; - } - /* parse here */ - parsesymbols(bufp); - if (morelines) { - do { - *symbuf = '\0'; - if (!fgets(symbuf, BUFSZ, fp)) { - morelines = FALSE; - break; - } - mungspaces(symbuf); - bufp = symbuf; - } while (*bufp == '#'); - } - } while (morelines); + if (!parsesymbols(bufp)) { + config_error_add("Error in SYMBOLS definition '%s'", bufp); + retval = FALSE; + } switch_symbols(TRUE); } else if (match_varname(buf, "WIZKIT", 6)) { (void) strncpy(wizkit, bufp, WIZKIT_MAX - 1); @@ -2522,31 +2712,38 @@ int src; } else if (match_varname(buf, "SOUND", 5)) { add_sound_mapping(bufp); #endif -#ifdef QT_GRAPHICS - /* These should move to wc_ options */ } else if (match_varname(buf, "QT_TILEWIDTH", 12)) { +#ifdef QT_GRAPHICS extern char *qt_tilewidth; if (qt_tilewidth == NULL) qt_tilewidth = dupstr(bufp); +#endif } else if (match_varname(buf, "QT_TILEHEIGHT", 13)) { +#ifdef QT_GRAPHICS extern char *qt_tileheight; if (qt_tileheight == NULL) qt_tileheight = dupstr(bufp); +#endif } else if (match_varname(buf, "QT_FONTSIZE", 11)) { +#ifdef QT_GRAPHICS extern char *qt_fontsize; if (qt_fontsize == NULL) qt_fontsize = dupstr(bufp); +#endif } else if (match_varname(buf, "QT_COMPACT", 10)) { +#ifdef QT_GRAPHICS extern int qt_compact_mode; qt_compact_mode = atoi(bufp); #endif - } else - return 0; - return 1; + } else { + config_error_add("Unknown config statement"); + return FALSE; + } + return retval; } #ifdef USER_SOUNDS @@ -2558,45 +2755,148 @@ const char *filename; } #endif /* USER_SOUNDS */ +struct _config_error_frame { + int line_num; + int num_errors; + boolean origline_shown; + boolean fromfile; + boolean secure; + char origline[4 * BUFSZ]; + char source[BUFSZ]; + struct _config_error_frame *next; +}; + +struct _config_error_frame *config_error_data = (struct _config_error_frame *)0; + +void +config_error_init(from_file, sourcename, secure) +boolean from_file; +const char *sourcename; +boolean secure; +{ + struct _config_error_frame *tmp = (struct _config_error_frame *) + alloc(sizeof(struct _config_error_frame)); + + tmp->line_num = 0; + tmp->num_errors = 0; + tmp->origline_shown = FALSE; + tmp->fromfile = from_file; + tmp->secure = secure; + tmp->origline[0] = '\0'; + if (sourcename && sourcename[0]) { + (void) strncpy(tmp->source, sourcename, sizeof(tmp->source)-1); + tmp->source[sizeof(tmp->source)-1] = '\0'; + } else + tmp->source[0] = '\0'; + + tmp->next = config_error_data; + config_error_data = tmp; +} + +STATIC_OVL boolean +config_error_nextline(line) +const char *line; +{ + struct _config_error_frame *ced = config_error_data; + + if (!ced) + return FALSE; + + if (ced->num_errors && ced->secure) + return FALSE; + + ced->line_num++; + ced->origline_shown = FALSE; + if (line && line[0]) { + (void) strncpy(ced->origline, line, sizeof(ced->origline)-1); + ced->origline[sizeof(ced->origline)-1] = '\0'; + } else + ced->origline[0] = '\0'; + + return TRUE; +} + +/*VARARGS1*/ +void config_error_add +VA_DECL(const char *, str) +{ + char buf[BUFSZ]; + char lineno[QBUFSZ]; + + VA_START(str); + VA_INIT(str, char *); + + Vsprintf(buf, str, VA_ARGS); + + if (!config_error_data) { + pline("%s.", *buf ? buf : "Unknown error"); + wait_synch(); + return; + } + + config_error_data->num_errors++; + if (!config_error_data->origline_shown + && !config_error_data->secure) { + pline("\n%s", config_error_data->origline); + config_error_data->origline_shown = TRUE; + } + if (config_error_data->line_num > 0 + && !config_error_data->secure) { + Sprintf(lineno, "Line %i: ", config_error_data->line_num); + } else + lineno[0] = '\0'; + pline("%s %s%s.", + config_error_data->secure ? "Error:" : " *", + lineno, + *buf ? buf : "Unknown error"); + + VA_END(); +} + +int +config_error_done() +{ + int n; + struct _config_error_frame *tmp = config_error_data; + + if (!config_error_data) + return 0; + n = config_error_data->num_errors; + if (n) { + pline("\n%i error%s in %s.\n", n, + (n > 1) ? "s" : "", + *config_error_data->source + ? config_error_data->source : configfile); + wait_synch(); + } + + config_error_data = tmp->next; + + free(tmp); + + return n; +} + boolean read_config_file(filename, src) const char *filename; int src; { - char buf[4 * BUFSZ], *p; FILE *fp; - boolean rv = TRUE; /* assume successful parse */ + boolean rv = TRUE; if (!(fp = fopen_config_file(filename, src))) return FALSE; /* begin detection of duplicate configfile options */ set_duplicate_opt_detection(1); + free_config_sections(); + iflags.parse_config_file_src = src; - while (fgets(buf, sizeof buf, fp)) { -#ifdef notyet -/* -XXX Don't call read() in parse_config_line, read as callback or reassemble -line at this level. -OR: Forbid multiline stuff for alternate config sources. -*/ -#endif - if ((p = index(buf, '\n')) != 0) - *p = '\0'; - if (!parse_config_line(fp, buf, src)) { - static const char badoptionline[] = "Bad option line: \"%s\""; - - /* truncate buffer if it's long; this is actually conservative */ - if (strlen(buf) > BUFSZ - sizeof badoptionline) - buf[BUFSZ - sizeof badoptionline] = '\0'; - - raw_printf(badoptionline, buf); - wait_synch(); - rv = FALSE; - } - } + rv = parse_conf_file(fp, parse_config_line); (void) fclose(fp); + free_config_sections(); /* turn off detection of duplicate configfile options */ set_duplicate_opt_detection(0); return rv; @@ -2700,47 +3000,171 @@ struct obj *obj; } } + +boolean +proc_wizkit_line(buf) +char *buf; +{ + struct obj *otmp = readobjnam(buf, (struct obj *) 0); + + if (otmp) { + if (otmp != &zeroobj) + wizkit_addinv(otmp); + } else { + /* .60 limits output line width to 79 chars */ + config_error_add("Bad wizkit item: \"%.60s\"", buf); + return FALSE; + } + return TRUE; +} + void read_wizkit() { FILE *fp; - char *ep, buf[BUFSZ]; - struct obj *otmp; - boolean bad_items = FALSE, skip = FALSE; if (!wizard || !(fp = fopen_wizkit_file())) return; program_state.wizkit_wishing = 1; - while (fgets(buf, (int) (sizeof buf), fp)) { - ep = index(buf, '\n'); + config_error_init(TRUE, "WIZKIT", FALSE); + + parse_conf_file(fp, proc_wizkit_line); + (void) fclose(fp); + + config_error_done(); + program_state.wizkit_wishing = 0; + + return; +} + +/* parse_conf_file + * + * Read from file fp, handling comments, empty lines, config sections, + * CHOOSE, and line continuation, calling proc for every valid line. + * + * Continued lines are merged together with one space in between. + */ +STATIC_OVL boolean +parse_conf_file(fp, proc) +FILE *fp; +boolean FDECL((*proc), (char *)); +{ + char inbuf[4 * BUFSZ]; + boolean rv = TRUE; /* assume successful parse */ + char *ep; + boolean skip = FALSE, morelines = FALSE; + char *buf = (char *) 0; + + free_config_sections(); + + while (fgets(inbuf, (int) (sizeof inbuf), fp)) { + ep = index(inbuf, '\n'); if (skip) { /* in case previous line was too long */ if (ep) skip = FALSE; /* found newline; next line is normal */ } else { - if (!ep) + if (!ep) { + config_error_add("Line too long, skipping"); skip = TRUE; /* newline missing; discard next fgets */ - else + } else { + char *tmpbuf = (char *) 0; + int len; + boolean ignoreline = FALSE; + boolean oldline = FALSE; + *ep = '\0'; /* remove newline */ - if (buf[0]) { - otmp = readobjnam(buf, (struct obj *) 0); - if (otmp) { - if (otmp != &zeroobj) - wizkit_addinv(otmp); - } else { - /* .60 limits output line width to 79 chars */ - raw_printf("Bad wizkit item: \"%.60s\"", buf); - bad_items = TRUE; + /* line continuation (trailing '\') */ + morelines = (--ep >= inbuf && *ep == '\\'); + if (morelines) + *ep = '\0'; + + /* trim off spaces at end of line */ + while (--ep >= inbuf && (*ep == ' ' || *ep == '\t' || *ep == '\r')) + *ep = '\0'; + + if (!config_error_nextline(inbuf)) { + rv = FALSE; + if (buf) + free(buf), buf = (char *) 0; + break; } + + ep = inbuf; + while (*ep == ' ' || *ep == '\t') ep++; + + /* lines beginning with '#' are comments. ignore empty lines. */ + if (!*ep || *ep == '#') + ignoreline = TRUE; + + if (buf) + oldline = TRUE; + + /* merge now read line with previous ones, if necessary */ + if (!ignoreline) { + len = strlen(inbuf) + 1; + if (buf) + len += strlen(buf); + tmpbuf = (char *) alloc(len); + if (buf) { + Sprintf(tmpbuf, "%s %s", buf, inbuf); + free(buf); + } else + Strcpy(tmpbuf, inbuf); + buf = tmpbuf; + } + + if (morelines || (ignoreline && !oldline)) + continue; + + if (handle_config_section(ep)) { + free(buf); + buf = (char *) 0; + continue; + } + + /* from here onwards, we'll handle buf only */ + + if (match_varname(buf, "CHOOSE", 6)) { + char *section; + char *bufp = find_optparam(buf); + if (!bufp) { + config_error_add("Format is CHOOSE=section1,section2,..."); + rv = FALSE; + free(buf); + buf = (char *) 0; + continue; + } + bufp++; + if (config_section_chosen) + free(config_section_chosen); + section = choose_random_part(bufp, ','); + if (section) + config_section_chosen = dupstr(section); + else { + config_error_add("No config section to choose"); + rv = FALSE; + } + free(buf); + buf = (char *) 0; + continue; + } + + if (!proc(buf)) + rv = FALSE; + + free(buf); + buf = (char *) 0; } } } - program_state.wizkit_wishing = 0; - if (bad_items) - wait_synch(); - (void) fclose(fp); - return; + + if (buf) + free(buf); + + free_config_sections(); + return rv; } extern struct symsetentry *symset_list; /* options.c */ @@ -2749,6 +3173,7 @@ extern const char *known_handling[]; /* drawing.c */ extern const char *known_restrictions[]; /* drawing.c */ static int symset_count = 0; /* for pick-list building only */ static boolean chosen_symset_start = FALSE, chosen_symset_end = FALSE; +static int symset_which_set = 0; STATIC_OVL FILE * @@ -2768,7 +3193,6 @@ int read_sym_file(which_set) int which_set; { - char buf[4 * BUFSZ]; FILE *fp; if (!(fp = fopen_sym_file())) @@ -2776,24 +3200,42 @@ int which_set; symset_count = 0; chosen_symset_start = chosen_symset_end = FALSE; - while (fgets(buf, 4 * BUFSZ, fp)) { - if (!parse_sym_line(buf, which_set)) { - raw_printf("Bad symbol line: \"%.50s\"", buf); - wait_synch(); - } - } + symset_which_set = which_set; + + config_error_init(TRUE, "symbols", FALSE); + + parse_conf_file(fp, proc_symset_line); (void) fclose(fp); - if (!chosen_symset_end && !chosen_symset_start) + + if (!chosen_symset_start && !chosen_symset_end) { + /* name caller put in symset[which_set].name was not found; + if it looks like "Default symbols", null it out and return + success to use the default; otherwise, return failure */ + if (symset[which_set].name + && (fuzzymatch(symset[which_set].name, "Default symbols", + " -_", TRUE) + || !strcmpi(symset[which_set].name, "default"))) + clear_symsetentry(which_set, TRUE); + config_error_done(); return (symset[which_set].name == 0) ? 1 : 0; - if (!chosen_symset_end) { - raw_printf("Missing finish for symset \"%s\"", + } + if (!chosen_symset_end) + config_error_add("Missing finish for symset \"%s\"", symset[which_set].name ? symset[which_set].name : "unknown"); - wait_synch(); - } + + config_error_done(); + return 1; } +boolean +proc_symset_line(buf) +char *buf; +{ + return !((boolean) parse_sym_line(buf, symset_which_set)); +} + /* returns 0 on error */ int parse_sym_line(buf, which_set) @@ -2807,16 +3249,14 @@ int which_set; /* convert each instance of whitespace (tabs, consecutive spaces) into a single space; leading and trailing spaces are stripped */ mungspaces(buf); - if (!*buf || *buf == '#' || !strcmp(buf, " ")) - return 1; - /* remove trailing comment, if any */ - if ((commentp = rindex(buf, '#')) != 0) { - *commentp = '\0'; - /* remove space preceding the stripped comment, if any; - we know 'commentp > buf' because *buf=='#' was caught above */ - if (commentp[-1] == ' ') - *--commentp = '\0'; - } + + /* remove trailing comment, if any (this isn't strictly needed for + individual symbols, and it won't matter if "X#comment" without + separating space slips through; for handling or set description, + symbol set creator is responsible for preceding '#' with a space + and that comment itself doesn't contain " #") */ + if ((commentp = rindex(buf, '#')) != 0 && commentp[-1] == ' ') + commentp[-1] = '\0'; /* find the '=' or ':' */ bufp = index(buf, '='); @@ -2831,6 +3271,7 @@ int which_set; chosen_symset_start = FALSE; return 1; } + config_error_add("No \"finish\""); return 0; } /* skip '=' and space which follows, if any */ @@ -2839,8 +3280,10 @@ int which_set; ++bufp; symp = match_sym(buf); - if (!symp) + if (!symp) { + config_error_add("Unknown sym keyword"); return 0; + } if (!symset[which_set].name) { /* A null symset name indicates that we're just @@ -3030,8 +3473,8 @@ const char *dir UNUSED_if_not_OS2_CODEVIEW; #ifdef VMS /* must be stream-lf to use UPDATE_RECORD_IN_PLACE */ if (!file_is_stmlf(fd)) { raw_printf( - "Warning: scoreboard file %s is not in stream_lf format", - fq_record); + "Warning: scoreboard file '%s' is not in stream_lf format", + fq_record); wait_synch(); } #endif @@ -3043,7 +3486,7 @@ const char *dir UNUSED_if_not_OS2_CODEVIEW; (void) chmod(fq_record, FCMASK | 007); #endif /* VMS && !SECURE */ } else { - raw_printf("Warning: cannot write scoreboard file %s", fq_record); + raw_printf("Warning: cannot write scoreboard file '%s'", fq_record); wait_synch(); } #endif /* !UNIX && !VMS */ @@ -3064,24 +3507,51 @@ const char *dir UNUSED_if_not_OS2_CODEVIEW; Strcpy(tmp, RECORD); fq_record = fqname(RECORD, SCOREPREFIX, 0); #endif +#ifdef WIN32 + /* If dir is NULL it indicates create but + only if it doesn't already exist */ + if (!dir) { + char buf[BUFSZ]; + + buf[0] = '\0'; + fd = open(fq_record, O_RDWR); + if (!(fd == -1 && errno == ENOENT)) { + if (fd >= 0) { + (void) nhclose(fd); + } else { + /* explanation for failure other than missing file */ + Sprintf(buf, "error \"%s\", (errno %d).", + fq_record, errno); + paniclog("scorefile", buf); + } + return; + } + Sprintf(buf, "missing \"%s\", creating new scorefile.", + fq_record); + paniclog("scorefile", buf); + } +#endif if ((fd = open(fq_record, O_RDWR)) < 0) { -/* try to create empty record */ + /* try to create empty 'record' */ #if defined(AZTEC_C) || defined(_DCC) \ || (defined(__GNUC__) && defined(__AMIGA__)) /* Aztec doesn't use the third argument */ /* DICE doesn't like it */ - if ((fd = open(fq_record, O_CREAT | O_RDWR)) < 0) { + fd = open(fq_record, O_CREAT | O_RDWR); #else - if ((fd = open(fq_record, O_CREAT | O_RDWR, S_IREAD | S_IWRITE)) - < 0) { + fd = open(fq_record, O_CREAT | O_RDWR, S_IREAD | S_IWRITE); #endif - raw_printf("Warning: cannot write record %s", tmp); + if (fd <= 0) { + raw_printf("Warning: cannot write record '%s'", tmp); wait_synch(); - } else + } else { (void) nhclose(fd); - } else /* open succeeded */ + } + } else { + /* open succeeded => 'record' exists */ (void) nhclose(fd); + } #else /* MICRO || WIN32*/ #ifdef MAC @@ -3113,6 +3583,11 @@ const char *reason; /* explanation */ program_state.in_paniclog = 1; lfile = fopen_datafile(PANICLOG, "a", TROUBLEPREFIX); if (lfile) { +#ifdef PANICLOG_FMT2 + (void) fprintf(lfile, "%ld %s: %s %s\n", + ubirthday, (plname ? plname : "(none)"), + type, reason); +#else time_t now = getnow(); int uid = getuid(); char playmode = wizard ? 'D' : discover ? 'X' : '-'; @@ -3120,6 +3595,7 @@ const char *reason; /* explanation */ (void) fprintf(lfile, "%s %08ld %06ld %d %c: %s %s\n", version_string(buf), yyyymmdd(now), hhmmss(now), uid, playmode, type, reason); +#endif /* !PANICLOG_FMT2 */ (void) fclose(lfile); } program_state.in_paniclog = 0; @@ -3219,6 +3695,7 @@ recover_savefile() raw_printf("\nError writing %s; recovery failed.", SAVEF); (void) nhclose(gfd); (void) nhclose(sfd); + (void) nhclose(lfd); delete_savefile(); return FALSE; } @@ -3228,6 +3705,7 @@ recover_savefile() SAVEF); (void) nhclose(gfd); (void) nhclose(sfd); + (void) nhclose(lfd); delete_savefile(); return FALSE; } @@ -3238,6 +3716,7 @@ recover_savefile() SAVEF); (void) nhclose(gfd); (void) nhclose(sfd); + (void) nhclose(lfd); delete_savefile(); return FALSE; } @@ -3247,13 +3726,15 @@ recover_savefile() SAVEF); (void) nhclose(gfd); (void) nhclose(sfd); + (void) nhclose(lfd); delete_savefile(); return FALSE; } if (!copy_bytes(lfd, sfd)) { - (void) nhclose(lfd); + (void) nhclose(gfd); (void) nhclose(sfd); + (void) nhclose(lfd); delete_savefile(); return FALSE; } @@ -3261,7 +3742,7 @@ recover_savefile() processed[savelev] = 1; if (!copy_bytes(gfd, sfd)) { - (void) nhclose(lfd); + (void) nhclose(gfd); (void) nhclose(sfd); delete_savefile(); return FALSE; @@ -3302,6 +3783,7 @@ recover_savefile() for (lev = 0; lev < 256; lev++) { if (processed[lev]) { const char *fq_lock; + set_levelfile_name(lock, lev); fq_lock = fqname(lock, LEVELPREFIX, 3); (void) unlink(fq_lock); @@ -3348,7 +3830,11 @@ assure_syscf_file() * VMS overrides open() usage with a macro which requires it. */ #ifndef VMS +# if defined(NOCWD_ASSUMPTIONS) && defined(WIN32) + fd = open(fqname(SYSCF_FILE, SYSCONFPREFIX, 0), O_RDONLY); +# else fd = open(SYSCF_FILE, O_RDONLY); +# endif #else fd = open(SYSCF_FILE, O_RDONLY, 0); #endif @@ -3447,7 +3933,7 @@ boolean wildcards; #define TITLESCOPE 2 #define PASSAGESCOPE 3 -#define MAXPASSAGES SIZE(context.novel.pasg) /* 30 */ +#define MAXPASSAGES SIZE(context.novel.pasg) /* 20 */ static int FDECL(choose_passage, (int, unsigned)); @@ -3455,24 +3941,35 @@ static int FDECL(choose_passage, (int, unsigned)); been chosen, reset the tracking to make all passages available again */ static int choose_passage(passagecnt, oid) -int passagecnt; /* total of available passages, 1..MAXPASSAGES */ +int passagecnt; /* total of available passages */ unsigned oid; /* book.o_id, used to determine whether re-reading same book */ { int idx, res; if (passagecnt < 1) return 0; - if (passagecnt > MAXPASSAGES) - passagecnt = MAXPASSAGES; /* if a different book or we've used up all the passages already, reset in order to have all 'passagecnt' passages available */ if (oid != context.novel.id || context.novel.count == 0) { + int i, range = passagecnt, limit = MAXPASSAGES; + context.novel.id = oid; - context.novel.count = passagecnt; - for (idx = 0; idx < MAXPASSAGES; idx++) - context.novel.pasg[idx] = (xchar) ((idx < passagecnt) ? idx + 1 - : 0); + if (range <= limit) { + /* collect all of the N indices */ + context.novel.count = passagecnt; + for (idx = 0; idx < MAXPASSAGES; idx++) + context.novel.pasg[idx] = (xchar) ((idx < passagecnt) + ? idx + 1 : 0); + } else { + /* collect MAXPASSAGES of the N indices */ + context.novel.count = MAXPASSAGES; + for (idx = i = 0; i < passagecnt; ++i, --range) + if (range > 0 && rn2(range) < limit) { + context.novel.pasg[idx++] = (xchar) (i + 1); + --limit; + } + } } idx = rn2(context.novel.count); @@ -3492,21 +3989,29 @@ char *nowin_buf; unsigned oid; /* book identifier */ { dlb *fp; - char *endp; char line[BUFSZ], lastline[BUFSZ]; int scope = 0; int linect = 0, passagecnt = 0, targetpassage = 0; +/*JP const char *badtranslation = "an incomprehensible foreign translation"; +*/ + const char *badtranslation = "•sŠ®‘S‚ÈŠO‘Œê–|–ó"; boolean matchedsection = FALSE, matchedtitle = FALSE; winid tribwin = WIN_ERR; boolean grasped = FALSE; boolean foundpassage = FALSE; + if (nowin_buf) + *nowin_buf = '\0'; + /* check for mandatories */ if (!tribsection || !tribtitle) { if (!nowin_buf) +/*JP pline("It's %s of \"%s\"!", badtranslation, tribtitle); +*/ + pline("‚±‚ê‚́u%sv‚Ì%s‚¾I", tribtitle, badtranslation); return grasped; } @@ -3517,7 +4022,10 @@ unsigned oid; /* book identifier */ if (!fp) { /* this is actually an error - cannot open tribute file! */ if (!nowin_buf) +/*JP pline("You feel too overwhelmed to continue!"); +*/ + pline("‚ ‚È‚½‚Í‘±‚¯‚ç‚ê‚È‚¢‚قLj³“|‚³‚ꂽI"); return grasped; } @@ -3543,19 +4051,15 @@ unsigned oid; /* book identifier */ *line = *lastline = '\0'; while (dlb_fgets(line, sizeof line, fp) != 0) { linect++; - if ((endp = index(line, '\n')) != 0) - *endp = 0; + (void) strip_newline(line); switch (line[0]) { case '%': - if (!strncmpi(&line[1], "section ", sizeof("section ") - 1)) { + if (!strncmpi(&line[1], "section ", sizeof "section " - 1)) { char *st = &line[9]; /* 9 from "%section " */ scope = SECTIONSCOPE; - if (!strcmpi(st, tribsection)) - matchedsection = TRUE; - else - matchedsection = FALSE; - } else if (!strncmpi(&line[1], "title ", sizeof("title ") - 1)) { + matchedsection = !strcmpi(st, tribsection) ? TRUE : FALSE; + } else if (!strncmpi(&line[1], "title ", sizeof "title " - 1)) { char *st = &line[7]; /* 7 from "%title " */ char *p1, *p2; @@ -3565,8 +4069,6 @@ unsigned oid; /* book identifier */ if ((p2 = index(p1, ')')) != 0) { *p2 = '\0'; passagecnt = atoi(p1); - if (passagecnt > MAXPASSAGES) - passagecnt = MAXPASSAGES; scope = TITLESCOPE; if (matchedsection && !strcmpi(st, tribtitle)) { matchedtitle = TRUE; @@ -3580,27 +4082,25 @@ unsigned oid; /* book identifier */ } } } else if (!strncmpi(&line[1], "passage ", - sizeof("passage ") - 1)) { + sizeof "passage " - 1)) { int passagenum = 0; char *st = &line[9]; /* 9 from "%passage " */ - while (*st == ' ' || *st == '\t') - st++; - if (*st && digit(*st) && (strlen(st) < 3)) - passagenum = atoi(st); - if (passagenum && (passagenum <= passagecnt)) { + mungspaces(st); + passagenum = atoi(st); + if (passagenum > 0 && passagenum <= passagecnt) { scope = PASSAGESCOPE; - if (matchedtitle && (passagenum == targetpassage)) { - if (!nowin_buf) + if (matchedtitle && passagenum == targetpassage) { + foundpassage = TRUE; + if (!nowin_buf) { tribwin = create_nhwindow(NHW_MENU); - else - foundpassage = TRUE; + if (tribwin == WIN_ERR) + goto cleanup; + } } } - } else if (!strncmpi(&line[1], "e ", sizeof("e ") - 1)) { - if (matchedtitle && scope == PASSAGESCOPE - && ((!nowin_buf && tribwin != WIN_ERR) - || (nowin_buf && foundpassage))) + } else if (!strncmpi(&line[1], "e ", sizeof "e " - 1)) { + if (foundpassage) goto cleanup; if (scope == TITLESCOPE) matchedtitle = FALSE; @@ -3617,13 +4117,16 @@ unsigned oid; /* book identifier */ /* comment only, next! */ break; default: - if (matchedtitle && scope == PASSAGESCOPE) { - if (!nowin_buf && tribwin != WIN_ERR) { + if (foundpassage) { + if (!nowin_buf) { + /* outputting multi-line passage to text window */ putstr(tribwin, 0, line); - Strcpy(lastline, line); - } else if (nowin_buf) { - if ((int) strlen(line) < bufsz - 1) - Strcpy(nowin_buf, line); + if (*line) + Strcpy(lastline, line); + } else { + /* fetching one-line passage into buffer */ + copynchars(nowin_buf, line, bufsz - 1); + goto cleanup; /* don't wait for "%e passage" */ } } } @@ -3631,26 +4134,33 @@ unsigned oid; /* book identifier */ cleanup: (void) dlb_fclose(fp); - if (!nowin_buf && tribwin != WIN_ERR) { - if (matchedtitle && scope == PASSAGESCOPE) { - display_nhwindow(tribwin, FALSE); - /* put the final attribution line into message history, - analogous to the summary line from long quest messages */ - if (index(lastline, '[')) - mungspaces(lastline); /* to remove leading spaces */ - else /* construct one if necessary */ - Sprintf(lastline, "[%s, by Terry Pratchett]", tribtitle); - putmsghistory(lastline, FALSE); - } - destroy_nhwindow(tribwin); - tribwin = WIN_ERR; - grasped = TRUE; + if (nowin_buf) { + /* one-line buffer */ + grasped = *nowin_buf ? TRUE : FALSE; } else { - if (!nowin_buf) - pline("It seems to be %s of \"%s\"!", badtranslation, tribtitle); - else - if (foundpassage) + if (tribwin != WIN_ERR) { /* implies 'foundpassage' */ + /* multi-line window, normal case; + if lastline is empty, there were no non-empty lines between + "%passage n" and "%e passage" so we leave 'grasped' False */ + if (*lastline) { + display_nhwindow(tribwin, FALSE); + /* put the final attribution line into message history, + analogous to the summary line from long quest messages */ + if (index(lastline, '[')) + mungspaces(lastline); /* to remove leading spaces */ + else /* construct one if necessary */ + Sprintf(lastline, "[%s, by Terry Pratchett]", tribtitle); + putmsghistory(lastline, FALSE); grasped = TRUE; + } + destroy_nhwindow(tribwin); + } + if (!grasped) + /* multi-line window, problem */ +/*JP + pline("It seems to be %s of \"%s\"!", badtranslation, tribtitle); +*/ + pline("‚±‚ê‚́u%sv‚Ì%s‚̂悤‚¾I", tribtitle, badtranslation); } return grasped; }