From b2961ea4a792fa7e5bead6e49d399dcea55b039b Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Tue, 30 Jun 2020 10:03:07 +0200 Subject: Keep track of current line number in config file parsing --- shared/config-parser.c | 212 +++++++++++++++++++++++++------------------------ 1 file changed, 108 insertions(+), 104 deletions(-) (limited to 'shared/config-parser.c') diff --git a/shared/config-parser.c b/shared/config-parser.c index 6c2d33d..850bae8 100644 --- a/shared/config-parser.c +++ b/shared/config-parser.c @@ -372,6 +372,7 @@ do { \ bool scfg_parse(struct server_config *scfg, char *buf, notification_cb_t notification_cb) { + unsigned lineno; char *pos; bool valid = true; @@ -379,7 +380,7 @@ bool scfg_parse(struct server_config *scfg, char *buf, pos = buf; - if (!config_parse_header(SERVER_CFG_HEADER, &pos)) { + if (!config_parse_header(SERVER_CFG_HEADER, &pos, &lineno)) { INVALID("missing/invalid header"); goto out; } @@ -390,7 +391,7 @@ bool scfg_parse(struct server_config *scfg, char *buf, struct cfg_value value; if (!config_parse_line(scfg->filename, &pos, scfg_key_map, &key, - &keyname, &value, !!notification_cb)) + &keyname, &value, !!notification_cb, &lineno)) break; /* FIXME: Improve error reporting */ @@ -603,33 +604,43 @@ bool scfg_init(struct server_config *scfg, const char *filename) return true; } -static void eat_whitespace_and_comments(char **pos) +static void eat_whitespace_and_comments(char **pos, unsigned *lineno) { - assert_return(pos && *pos); + assert_return(pos && *pos && lineno); while (true) { - while (isspace(**pos)) + while (**pos == ' ' || + **pos == '\f' || + **pos == '\r' || + **pos == '\t' || + **pos == '\v') (*pos)++; + if (**pos == '\n') { + (*lineno)++; + (*pos)++; + continue; + } + if (**pos != '#') return; - while (**pos != '\r' && **pos != '\n' && **pos != '\0') + while (**pos != '\n' && **pos != '\0') (*pos)++; } } -static char *get_line(char **pos) +static char *get_line(char **pos, unsigned *lineno) { char *begin, *end; - assert_return(pos && *pos, NULL); + assert_return(pos && *pos && lineno, NULL); - eat_whitespace_and_comments(pos); + (*lineno)++; + + eat_whitespace_and_comments(pos, lineno); begin = *pos; - while (isspace(*begin)) - begin++; if (*begin == '\0') return NULL; @@ -916,155 +927,148 @@ error: bool config_parse_line(const char *filename, char **buf, struct cfg_key_value_map *kvmap, int *rkey, const char **rkeyname, struct cfg_value *rvalue, - bool async_dns) + bool async_dns, unsigned *lineno) { - char *line, *tmp, *key; + char *line, *pos, *key, *value; + size_t linelen; int i; - assert_return(buf && *buf && kvmap && rkey && rkeyname && rvalue, + assert_return(buf && *buf && kvmap && rkey && rkeyname && rvalue && lineno, false); - line = get_line(buf); + line = get_line(buf, lineno); if (!line) return false; - debug(DBG_CFG, "%s: parsing config line: %s", filename, line); + debug(DBG_CFG, "%s: parsing cfg line %u: %s", filename, *lineno, line); - tmp = line; - while (isspace(*tmp)) - tmp++; + linelen = strlen(line); + key = line; - if (*tmp == '\0') + pos = memchr(line, '=', linelen); + if (!pos) goto error; - key = tmp; - while (*tmp != '\0' && !isspace(*tmp)) - tmp++; + value = pos + 1; + *pos = '\0'; - if (*tmp == '\0') - goto error; - - *tmp = '\0'; - tmp++; + while (isspace(*(--pos))) + *pos = '\0'; - while (isspace(*tmp)) - tmp++; + while (isspace(*value)) + value++; - if (*tmp != '=') + if (*value == '\0') goto error; - tmp++; - while (isspace(*tmp)) - tmp++; + for (i = 0; kvmap[i].key_name; i++) { + if (streq(kvmap[i].key_name, key)) + break; + } - if (*tmp == '\0') + if (!kvmap[i].key_name) goto error; - for (i = 0; kvmap[i].key_name; i++) { - if (!streq(kvmap[i].key_name, key)) - continue; + switch (kvmap[i].value_type) { + case CFG_VAL_TYPE_STRING: + rvalue->type = CFG_VAL_TYPE_STRING; + rvalue->str = value; + break; - switch (kvmap[i].value_type) { - case CFG_VAL_TYPE_STRING: - rvalue->type = CFG_VAL_TYPE_STRING; - rvalue->str = tmp; - break; + case CFG_VAL_TYPE_UINT16: { + uint16_t v; - case CFG_VAL_TYPE_UINT16: { - uint16_t v; + if (strtou16_strict(value, &v) < 0) + goto error; - if (strtou16_strict(tmp, &v) < 0) - goto error; + rvalue->type = CFG_VAL_TYPE_UINT16; + rvalue->uint16 = v; + break; + } - rvalue->type = CFG_VAL_TYPE_UINT16; - rvalue->uint16 = v; - break; - } + case CFG_VAL_TYPE_ASYNC_ADDRS: + error("CFG_VAL_TYPE_ASYNC_ADDRS is a return value"); + _fallthrough_; - case CFG_VAL_TYPE_ASYNC_ADDRS: - error("CFG_VAL_TYPE_ASYNC_ADDRS is a return value"); - _fallthrough_; + case CFG_VAL_TYPE_ADDRS: + if (!strtosockaddrs(value, rvalue, async_dns)) + goto error; + switch (rvalue->type) { case CFG_VAL_TYPE_ADDRS: - if (!strtosockaddrs(tmp, rvalue, async_dns)) + if (list_empty(&rvalue->saddrs)) { + error("empty address list"); goto error; + } + break; - switch (rvalue->type) { - case CFG_VAL_TYPE_ADDRS: - if (list_empty(&rvalue->saddrs)) { - error("empty address list"); - goto error; - } - break; - - case CFG_VAL_TYPE_ASYNC_ADDRS: - if (!async_dns) { - error("unexpected async dns result"); - goto error; - } - - if (!rvalue->dns_async) { - error("dns_async not set"); - goto error; - } - break; - - default: - error("invalid type returned from strtosockaddrs"); + case CFG_VAL_TYPE_ASYNC_ADDRS: + if (!async_dns) { + error("unexpected async dns result"); goto error; } - break; - case CFG_VAL_TYPE_BOOL: - if (strcaseeq(tmp, "yes") || strcaseeq(tmp, "true")) { - rvalue->type = CFG_VAL_TYPE_BOOL; - rvalue->boolean = true; - } else if (strcaseeq(tmp, "no") || - strcaseeq(tmp, "false")) { - rvalue->type = CFG_VAL_TYPE_BOOL; - rvalue->boolean = false; - } else { - error("invalid boolean value (%s)", tmp); + if (!rvalue->dns_async) { + error("dns_async not set"); goto error; } break; - case CFG_VAL_TYPE_INVALID: - _fallthrough_; default: + error("invalid type returned from strtosockaddrs"); goto error; } + break; - /* sanity check */ - if ((rvalue->type != kvmap[i].value_type) && - ((kvmap[i].value_type != CFG_VAL_TYPE_ASYNC_ADDRS) && - (rvalue->type != CFG_VAL_TYPE_ADDRS))) { - error("rvalue->type != kvmap->type"); + case CFG_VAL_TYPE_BOOL: + if (strcaseeq(value, "yes") || strcaseeq(value, "true")) { + rvalue->type = CFG_VAL_TYPE_BOOL; + rvalue->boolean = true; + } else if (strcaseeq(value, "no") || + strcaseeq(value, "false")) { + rvalue->type = CFG_VAL_TYPE_BOOL; + rvalue->boolean = false; + } else { + error("invalid boolean value (%s)", value); goto error; } + break; - *rkey = kvmap[i].key_value; - *rkeyname = kvmap[i].key_name; - return true; + case CFG_VAL_TYPE_INVALID: + _fallthrough_; + default: + goto error; } + /* sanity check */ + if ((rvalue->type != kvmap[i].value_type) && + ((kvmap[i].value_type != CFG_VAL_TYPE_ASYNC_ADDRS) && + (rvalue->type != CFG_VAL_TYPE_ADDRS))) { + error("rvalue->type != kvmap->type"); + goto error; + } + + *rkey = kvmap[i].key_value; + *rkeyname = kvmap[i].key_name; + return true; + error: - /* FIXME: the line is already mangled here, a line number would be nice - */ - error("%s: invalid config line: %s", filename, line); + error("%s: invalid config line: %u:%s", filename, *lineno, line); rvalue->type = CFG_VAL_TYPE_INVALID; *rkey = 0; *rkeyname = NULL; return true; } -bool config_parse_header(const char *title, char **buf) +bool config_parse_header(const char *title, char **buf, unsigned *lineno) { char *line; - assert_return(!empty_str(title) && buf && *buf, false); + assert_return(!empty_str(title) && buf && *buf && lineno, false); + + *lineno = 0; - line = get_line(buf); + line = get_line(buf, lineno); if (line) { char titlehdr[strlen(title) + 3]; -- cgit v1.2.3