-/* 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"
#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
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()
*
*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;
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, "\92n\89º%d\8aK\82Ì\83t\83@\83C\83\8b\"%s\"\82ð\8aJ\82¯\82È\82¢(errno %d)\81D",
+ lev, lock, errno);
+#endif
return fd;
}
int lev, oflag;
{
int reslt, fd;
+
if (!lftrack.init) {
lftrack.init = 1;
lftrack.fd = -1;
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;
}
}
return close(fd);
}
-#else
+#else /* !HOLD_LOCKFILE_OPEN */
int
nhclose(fd)
{
return close(fd);
}
-#endif
+#endif /* ?HOLD_LOCKFILE_OPEN */
/* ---------- END LEVEL FILE HANDLING ----------- */
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
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
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);
}
}
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);
#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;
/* ---------- BEGIN CONFIG FILE HANDLING ----------- */
-const char *configfile =
+const char *default_configfile =
#ifdef UNIX
".nethackrc";
#else
#endif
/* used for messaging */
-char lastconfigfile[BUFSZ];
+char configfile[BUFSZ];
#ifdef MSDOS
/* conflict with speed-dial under windows
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
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"\82ð\97D\90æ\82µ\82Ä\93Ç\82Ý\8d\9e\82Ý*/
+ 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 */
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:
}
#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;
#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 == ' ')
/* 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);
} 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)
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);
&& 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;
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;
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)
} 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)
#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);
} 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
}
#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;
}
}
+
+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 */
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 *
read_sym_file(which_set)
int which_set;
{
- char buf[4 * BUFSZ];
FILE *fp;
if (!(fp = fopen_sym_file()))
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)
/* 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, '=');
chosen_symset_start = FALSE;
return 1;
}
+ config_error_add("No \"finish\"");
return 0;
}
/* skip '=' and space which follows, if any */
++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
#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
(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 */
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
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' : '-';
(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;
raw_printf("\nError writing %s; recovery failed.", SAVEF);
(void) nhclose(gfd);
(void) nhclose(sfd);
+ (void) nhclose(lfd);
delete_savefile();
return FALSE;
}
SAVEF);
(void) nhclose(gfd);
(void) nhclose(sfd);
+ (void) nhclose(lfd);
delete_savefile();
return FALSE;
}
SAVEF);
(void) nhclose(gfd);
(void) nhclose(sfd);
+ (void) nhclose(lfd);
delete_savefile();
return FALSE;
}
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;
}
processed[savelev] = 1;
if (!copy_bytes(gfd, sfd)) {
- (void) nhclose(lfd);
+ (void) nhclose(gfd);
(void) nhclose(sfd);
delete_savefile();
return FALSE;
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);
* 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
#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));
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);
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 = "\95s\8a®\91S\82È\8aO\8d\91\8cê\96|\96ó";
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("\82±\82ê\82Í\81u%s\81v\82Ì%s\82¾\81I", tribtitle, badtranslation);
return grasped;
}
if (!fp) {
/* this is actually an error - cannot open tribute file! */
if (!nowin_buf)
+/*JP
pline("You feel too overwhelmed to continue!");
+*/
+ pline("\82 \82È\82½\82Í\91±\82¯\82ç\82ê\82È\82¢\82Ù\82Ç\88³\93|\82³\82ê\82½\81I");
return grasped;
}
*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;
if ((p2 = index(p1, ')')) != 0) {
*p2 = '\0';
passagecnt = atoi(p1);
- if (passagecnt > MAXPASSAGES)
- passagecnt = MAXPASSAGES;
scope = TITLESCOPE;
if (matchedsection && !strcmpi(st, tribtitle)) {
matchedtitle = TRUE;
}
}
} 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;
/* 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" */
}
}
}
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("\82±\82ê\82Í\81u%s\81v\82Ì%s\82Ì\82æ\82¤\82¾\81I", tribtitle, badtranslation);
}
return grasped;
}