summaryrefslogtreecommitdiff
path: root/src/mmo/config_parse.cpp
diff options
context:
space:
mode:
authorBen Longbons <b.r.longbons@gmail.com>2015-01-06 17:31:21 -0800
committerBen Longbons <b.r.longbons@gmail.com>2015-01-22 23:33:13 -0800
commit2cd52ab17ee1b830bc53321b112411122dddc1c8 (patch)
tree43666d3295c55ae6b395c5d8cd3fd026e71468a3 /src/mmo/config_parse.cpp
parente1418f378c66343a35db3791cbf0d54a4be3fbd3 (diff)
downloadtmwa-2cd52ab17ee1b830bc53321b112411122dddc1c8.tar.gz
tmwa-2cd52ab17ee1b830bc53321b112411122dddc1c8.tar.bz2
tmwa-2cd52ab17ee1b830bc53321b112411122dddc1c8.tar.xz
tmwa-2cd52ab17ee1b830bc53321b112411122dddc1c8.zip
Use Spanned<T> while parsing config
Diffstat (limited to 'src/mmo/config_parse.cpp')
-rw-r--r--src/mmo/config_parse.cpp210
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
}