From: Pasi Kallinen Date: Wed, 13 Sep 2017 09:20:20 +0000 (+0300) Subject: Handle config file reading at single function X-Git-Tag: NetHack-3.6.1_RC01~372 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2c688a12d5620880e03d8f971729174fd29a770d;p=nethack Handle config file reading at single function The function handles comments, empty lines, config sections, CHOOSE, and line continuation, while calling another function for other lines. Currently used for config file, sysconf, and WIZKIT. --- diff --git a/include/flag.h b/include/flag.h index a51eb68ef..4cd9e1641 100644 --- a/include/flag.h +++ b/include/flag.h @@ -191,6 +191,7 @@ struct instance_flags { * behaviour of various NetHack functions and probably warrant * a structure of their own elsewhere some day. */ + int parse_config_file_src; /* hack for parse_config_line() */ int in_lava_effects; /* hack for Boots_off() */ int last_msg; /* indicator of last message player saw */ int purge_monsters; /* # of dead monsters still on fmon list */ diff --git a/src/files.c b/src/files.c index 7d4278e26..12ec30462 100644 --- a/src/files.c +++ b/src/files.c @@ -192,9 +192,10 @@ 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 *, char *, uchar *, BOOLEAN_P, int, const char *)); -int FDECL(parse_config_line, (FILE *, char *, int)); +boolean FDECL(parse_config_line, (char *)); +STATIC_DCL boolean FDECL(parse_conf_file, (FILE *, boolean (*proc)(char *))); STATIC_DCL FILE *NDECL(fopen_sym_file); STATIC_DCL void FDECL(set_symhandling, (char *, int)); #ifdef NOCWD_ASSUMPTIONS @@ -2047,14 +2048,13 @@ int src; } /* - * 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 */ +get_uchars(buf, bufp, list, modlist, size, name) char *buf; /* read buffer, must be of size BUFSZ */ char *bufp; /* current pointer */ uchar *list; /* return list */ @@ -2101,13 +2101,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: @@ -2228,11 +2222,24 @@ char *buf; #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; @@ -2243,24 +2250,16 @@ int src; char *bufp, *altp, buf[4 * BUFSZ]; uchar translate[MAXPCHARS]; int len; - int retval = 1; + 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; - - if (src != SET_IN_SYS && handle_config_section(buf)) - return 1; /* find the '=' or ':' */ - bufp = index(buf, '='); - altp = index(buf, ':'); - if (!bufp || (altp && altp < bufp)) - bufp = altp; + bufp = find_optparam(buf); if (!bufp) { config_error_add("Not a config statement, missing '='"); return 0; @@ -2278,35 +2277,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 */ if (!parseoptions(bufp, TRUE, TRUE)) - retval = 0; - } else if (match_varname(buf, "CHOOSE", 6)) { - char *section; - 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"); - retval = 0; - } + 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 = 0; + retval = FALSE; } else if (match_varname(buf, "AUTOCOMPLETE", 5)) { parseautocomplete(bufp, TRUE); } else if (match_varname(buf, "MSGTYPE", 7)) { if (!msgtype_parse_add(bufp)) - retval = 0; + retval = FALSE; #ifdef NOCWD_ASSUMPTIONS } else if (match_varname(buf, "HACKDIR", 4)) { adjust_prefix(bufp, HACKPREFIX); @@ -2549,46 +2534,20 @@ int src; #endif /* SYSCF */ } else if (match_varname(buf, "BOULDER", 3)) { - (void) get_uchars(fp, buf, bufp, &iflags.bouldersym, TRUE, 1, + (void) get_uchars(buf, bufp, &iflags.bouldersym, TRUE, 1, "BOULDER"); } else if (match_varname(buf, "MENUCOLOR", 9)) { if (!add_menu_coloring(bufp)) - retval = 0; + retval = FALSE; } else if (match_varname(buf, "WARNINGS", 5)) { - (void) get_uchars(fp, buf, bufp, translate, FALSE, WARNCOUNT, + (void) get_uchars(buf, 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 */ - if (!parsesymbols(bufp)) { - raw_printf("Error in SYMBOLS definition '%s'.\n", bufp); - wait_synch(); - } - 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); @@ -2847,23 +2806,9 @@ int src; /* 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)) { - strip_newline(buf); - if (!config_error_nextline(buf)) { - rv = FALSE; - break; - } -#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 (!parse_config_line(fp, buf, src)) - rv = FALSE; - } + rv = parse_conf_file(fp, parse_config_line); (void) fclose(fp); free_config_sections(); @@ -2970,47 +2915,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 */