diff options
Diffstat (limited to 'src/mmo/config_parse.cpp')
-rw-r--r-- | src/mmo/config_parse.cpp | 210 |
1 files changed, 174 insertions, 36 deletions
diff --git a/src/mmo/config_parse.cpp b/src/mmo/config_parse.cpp index b47ee79..6e43471 100644 --- a/src/mmo/config_parse.cpp +++ b/src/mmo/config_parse.cpp @@ -23,6 +23,8 @@ #include "../strings/xstring.hpp" #include "../strings/zstring.hpp" +#include "../compat/borrow.hpp" + #include "../io/cxxstdio.hpp" #include "../io/extract.hpp" #include "../io/line.hpp" @@ -40,39 +42,138 @@ bool is_comment(XString line) } template<class ZS> +static +bool do_split(io::Spanned<ZS> line, io::Spanned<XString> *key, io::Spanned<ZS> *value) +{ + typename ZS::iterator colon = std::find(line.data.begin(), line.data.end(), ':'); + if (colon == line.data.end()) + return false; + key->data = line.data.xislice_h(colon); + key->span = line.span; + key->span.end.column = key->span.begin.column + key->data.size() - 1; + ++colon; + value->data = line.data.xislice_t(colon); + value->span = line.span; + value->span.begin.column = value->span.end.column - value->data.size() + 1; + return true; +} + +template<class ZS> +static +io::Spanned<ZS> do_lstrip(io::Spanned<ZS> value) +{ + io::Spanned<ZS> rv; + rv.data = value.data.lstrip(); + rv.span = value.span; + rv.span.begin.column += (value.data.size() - rv.data.size()); + return rv; +} + +static +io::Spanned<XString> do_rstrip(io::Spanned<XString> value) +{ + io::Spanned<XString> rv; + rv.data = value.data.rstrip(); + rv.span = value.span; + rv.span.end.column -= (value.data.size() - rv.data.size()); + return rv; +} + +static +io::Spanned<XString> do_strip(io::Spanned<XString> value) +{ + return do_lstrip(do_rstrip(value)); +} + +template<class ZS> inline -bool config_split_impl(ZS line, XString *key, ZS *value) +bool config_split_impl(io::Spanned<ZS> line, io::Spanned<XString> *key, io::Spanned<ZS> *value) { // unconditionally fail if line contains control characters - if (std::find_if(line.begin(), line.end(), + if (std::find_if(line.data.begin(), line.data.end(), [](unsigned char c) { return c < ' '; } - ) != line.end()) - return false; - // try to find a colon, fail if not found - typename ZS::iterator colon = std::find(line.begin(), line.end(), ':'); - if (colon == line.end()) + ) != line.data.end()) return false; - *key = line.xislice_h(colon).strip(); - // move past the colon and any spaces - ++colon; - *value = line.xislice_t(colon).lstrip(); + if (!do_split(line, key, value)) + return false; + *key = do_strip(*key); + *value = do_lstrip(*value); return true; } // eventually this should go away -bool config_split(ZString line, XString *key, ZString *value) +// currently the only real offenders are io::FD::open and *PRINTF +bool config_split(io::Spanned<ZString> line, io::Spanned<XString> *key, io::Spanned<ZString> *value) { return config_split_impl(line, key, value); } -// and use this instead -bool config_split(XString line, XString *key, XString *value) + +static +bool check_version(io::Spanned<XString> avers, Borrowed<bool> valid) { - return config_split_impl(line, key, value); + enum + { + GE, LE, GT, LT + } cmp; + + if (avers.data.startswith(">="_s)) + { + cmp = GE; + avers.data = avers.data.xslice_t(2); + avers.span.begin.column += 2; + } + else if (avers.data.startswith('>')) + { + cmp = GT; + avers.data = avers.data.xslice_t(1); + avers.span.begin.column += 1; + } + else if (avers.data.startswith("<="_s)) + { + cmp = LE; + avers.data = avers.data.xslice_t(2); + avers.span.begin.column += 2; + } + else if (avers.data.startswith('<')) + { + cmp = LT; + avers.data = avers.data.xslice_t(1); + avers.span.begin.column += 1; + } + else + { + avers.span.error("Version check must begin with one of: '>=', '>', '<=', '<'"_s); + *valid = false; + return false; + } + + Version vers; + if (!extract(avers.data, &vers)) + { + avers.span.error("Bad value"_s); + *valid = false; + return false; + } + switch (cmp) + { + case GE: + return CURRENT_VERSION >= vers; + case GT: + return CURRENT_VERSION > vers; + case LE: + return CURRENT_VERSION <= vers; + case LT: + return CURRENT_VERSION < vers; + } + abort(); } /// Master config parser. This handles 'import' and 'version-ge' etc. /// Then it defers to the inferior parser for a line it does not understand. +/// +/// Note: old-style 'version-ge: 1.2.3' etc apply to the rest of the file, but +/// new-style 'version: >= 1.2.3' apply only up to the next 'version:' bool load_config_file(ZString filename, ConfigItemParser slave) { io::LineReader in(filename); @@ -81,30 +182,66 @@ bool load_config_file(ZString filename, ConfigItemParser slave) PRINTF("Unable to open file: %s\n"_fmt, filename); return false; } - io::Line line; + io::Line line_; bool rv = true; - while (in.read_line(line)) + bool good_version = true; + while (in.read_line(line_)) { - if (is_comment(line.text)) + if (is_comment(line_.text)) continue; - XString key; - ZString value; - if (!config_split(line.text, &key, &value)) + auto line = io::respan(line_.to_span(), ZString(line_.text)); + io::Spanned<XString> key; + io::Spanned<ZString> value; + if (!config_split(line, &key, &value)) { - line.error("Bad config line"_s); + line.span.error("Bad config line"_s); rv = false; continue; } - if (key == "import"_s) + if (key.data == "version"_s) + { + if (value.data == "all"_s) + { + good_version = true; + } + else + { + good_version = true; + while (good_version && value.data) + { + ZString::iterator it = std::find(value.data.begin(), value.data.end(), ' '); + io::Spanned<XString> value_head; + value_head.data = value.data.xislice_h(it); + value_head.span = value.span; + value.data = value.data.xislice_t(it).lstrip(); + + value_head.span.end.column = value_head.span.begin.column + value_head.data.size() - 1; + value.span.begin.column = value.span.end.column - value.data.size() + 1; + + good_version &= check_version(value_head, borrow(rv)); + } + } + continue; + } + if (!good_version) { - rv &= load_config_file(value, slave); continue; } - else if (key == "version-lt"_s) + if (key.data == "import"_s) + { + if (!load_config_file(value.data, slave)) + { + value.span.error("Failed to include file"_s); + rv = false; + } + continue; + } + else if (key.data == "version-lt"_s) { Version vers; - if (!extract(value, &vers)) + if (!extract(value.data, &vers)) { + value.span.error("Bad value"_s); rv = false; continue; } @@ -112,47 +249,48 @@ bool load_config_file(ZString filename, ConfigItemParser slave) continue; break; } - else if (key == "version-le"_s) + else if (key.data == "version-le"_s) { Version vers; - if (!extract(value, &vers)) + if (!extract(value.data, &vers)) { rv = false; + value.span.error("Bad value"_s); continue; } if (CURRENT_VERSION <= vers) continue; break; } - else if (key == "version-gt"_s) + else if (key.data == "version-gt"_s) { Version vers; - if (!extract(value, &vers)) + if (!extract(value.data, &vers)) { rv = false; + value.span.error("Bad value"_s); continue; } if (CURRENT_VERSION > vers) continue; break; } - else if (key == "version-ge"_s) + else if (key.data == "version-ge"_s) { Version vers; - if (!extract(value, &vers)) + if (!extract(value.data, &vers)) { rv = false; + value.span.error("Bad value"_s); continue; } if (CURRENT_VERSION >= vers) continue; break; } - else if (!slave(key, value)) + else { - line.error("Bad config key or value"_s); - rv = false; - continue; + rv &= slave(key, value); } // nothing to see here, move along } |