Handle config file reading at single function
authorPasi Kallinen <paxed@alt.org>
Wed, 13 Sep 2017 09:20:20 +0000 (12:20 +0300)
committerPasi Kallinen <paxed@alt.org>
Wed, 13 Sep 2017 09:27:38 +0000 (12:27 +0300)
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.

include/flag.h
src/files.c

index a51eb68ef52fe619b3f084b23fe41b5f7c5a1f80..4cd9e1641d660077de61c1e20b8cb476dafa4c28 100644 (file)
@@ -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 */
index 7d4278e26391780367575c6b97c7121daa309aa3..12ec30462738b244218f3eaa18220faec89ed52b 100644 (file)
@@ -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 */