summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/admin/ladmin.cpp23
-rw-r--r--src/char/char.cpp131
-rw-r--r--src/char/inter.cpp21
-rw-r--r--src/char/inter.hpp2
-rw-r--r--src/io/span.cpp14
-rw-r--r--src/io/span.hpp2
-rw-r--r--src/login/login.cpp161
-rw-r--r--src/map/atcommand.cpp36
-rw-r--r--src/map/battle.cpp50
-rw-r--r--src/map/map.cpp155
-rw-r--r--src/mmo/config_parse.cpp210
-rw-r--r--src/mmo/config_parse.hpp5
-rw-r--r--src/mmo/config_parse_test.cpp60
-rw-r--r--src/mmo/version.cpp24
-rw-r--r--src/monitor/main.cpp44
15 files changed, 564 insertions, 374 deletions
diff --git a/src/admin/ladmin.cpp b/src/admin/ladmin.cpp
index 97b570e..774d8cd 100644
--- a/src/admin/ladmin.cpp
+++ b/src/admin/ladmin.cpp
@@ -37,6 +37,7 @@
#include "../io/cxxstdio.hpp"
#include "../io/extract.hpp"
#include "../io/read.hpp"
+#include "../io/span.hpp"
#include "../io/tty.hpp"
#include "../io/write.hpp"
@@ -2495,16 +2496,16 @@ int Connect_login_server(void)
}
static
-bool admin_confs(XString w1, ZString w2)
+bool admin_confs(io::Spanned<XString> w1, io::Spanned<ZString> w2)
{
{
- if (w1 == "login_ip"_s)
+ if (w1.data == "login_ip"_s)
{
- struct hostent *h = gethostbyname(w2.c_str());
+ struct hostent *h = gethostbyname(w2.data.c_str());
if (h != nullptr)
{
Iprintf("Login server IP address: %s -> %s\n"_fmt,
- w2, login_ip);
+ w2.data, login_ip);
login_ip = IP4Address({
static_cast<uint8_t>(h->h_addr[0]),
static_cast<uint8_t>(h->h_addr[1]),
@@ -2513,21 +2514,21 @@ bool admin_confs(XString w1, ZString w2)
});
}
}
- else if (w1 == "login_port"_s)
+ else if (w1.data == "login_port"_s)
{
- login_port = atoi(w2.c_str());
+ login_port = atoi(w2.data.c_str());
}
- else if (w1 == "admin_pass"_s)
+ else if (w1.data == "admin_pass"_s)
{
- admin_pass = stringish<AccountPass>(w2);
+ admin_pass = stringish<AccountPass>(w2.data);
}
- else if (w1 == "ladmin_log_filename"_s)
+ else if (w1.data == "ladmin_log_filename"_s)
{
- ladmin_log_filename = w2;
+ ladmin_log_filename = w2.data;
}
else
{
- PRINTF("WARNING: unknown ladmin config key: %s\n"_fmt, AString(w1));
+ PRINTF("WARNING: unknown ladmin config key: %s\n"_fmt, AString(w1.data));
return false;
}
}
diff --git a/src/char/char.cpp b/src/char/char.cpp
index a82a692..ff9905f 100644
--- a/src/char/char.cpp
+++ b/src/char/char.cpp
@@ -52,6 +52,7 @@
#include "../io/extract.hpp"
#include "../io/lock.hpp"
#include "../io/read.hpp"
+#include "../io/span.hpp"
#include "../io/tty.hpp"
#include "../io/write.hpp"
@@ -2737,15 +2738,15 @@ void check_connect_login_server(TimerData *, tick_t)
// Reading Lan Support configuration by [Yor]
//-------------------------------------------
static
-bool char_lan_config(XString w1, ZString w2)
+bool char_lan_config(io::Spanned<XString> w1, io::Spanned<ZString> w2)
{
struct hostent *h = nullptr;
{
- if (w1 == "lan_map_ip"_s)
+ if (w1.data == "lan_map_ip"_s)
{
// Read map-server Lan IP Address
- h = gethostbyname(w2.c_str());
+ h = gethostbyname(w2.data.c_str());
if (h != nullptr)
{
lan_map_ip = IP4Address({
@@ -2757,17 +2758,17 @@ bool char_lan_config(XString w1, ZString w2)
}
else
{
- PRINTF("Bad IP value: %s\n"_fmt, w2);
+ PRINTF("Bad IP value: %s\n"_fmt, w2.data);
return false;
}
PRINTF("LAN IP of map-server: %s.\n"_fmt, lan_map_ip);
}
- else if (w1 == "subnet"_s /*backward compatibility*/
- || w1 == "lan_subnet"_s)
+ else if (w1.data == "subnet"_s /*backward compatibility*/
+ || w1.data == "lan_subnet"_s)
{
- if (!extract(w2, &lan_subnet))
+ if (!extract(w2.data, &lan_subnet))
{
- PRINTF("Bad IP mask: %s\n"_fmt, w2);
+ PRINTF("Bad IP mask: %s\n"_fmt, w2.data);
return false;
}
PRINTF("Sub-network of the map-server: %s.\n"_fmt,
@@ -2798,23 +2799,23 @@ bool lan_check()
}
static
-bool char_config(XString w1, ZString w2)
+bool char_config(io::Spanned<XString> w1, io::Spanned<ZString> w2)
{
struct hostent *h = nullptr;
{
- if (w1 == "userid"_s)
- userid = stringish<AccountName>(w2);
- else if (w1 == "passwd"_s)
- passwd = stringish<AccountPass>(w2);
- else if (w1 == "server_name"_s)
+ if (w1.data == "userid"_s)
+ userid = stringish<AccountName>(w2.data);
+ else if (w1.data == "passwd"_s)
+ passwd = stringish<AccountPass>(w2.data);
+ else if (w1.data == "server_name"_s)
{
- server_name = stringish<ServerName>(w2);
- PRINTF("%s server has been intialized\n"_fmt, w2);
+ server_name = stringish<ServerName>(w2.data);
+ PRINTF("%s server has been intialized\n"_fmt, w2.data);
}
- else if (w1 == "login_ip"_s)
+ else if (w1.data == "login_ip"_s)
{
- h = gethostbyname(w2.c_str());
+ h = gethostbyname(w2.data.c_str());
if (h != nullptr)
{
login_ip = IP4Address({
@@ -2824,21 +2825,21 @@ bool char_config(XString w1, ZString w2)
static_cast<uint8_t>(h->h_addr[3]),
});
PRINTF("Login server IP address : %s -> %s\n"_fmt,
- w2, login_ip);
+ w2.data, login_ip);
}
else
{
- PRINTF("Bad IP value: %s\n"_fmt, w2);
+ PRINTF("Bad IP value: %s\n"_fmt, w2.data);
return false;
}
}
- else if (w1 == "login_port"_s)
+ else if (w1.data == "login_port"_s)
{
- login_port = atoi(w2.c_str());
+ login_port = atoi(w2.data.c_str());
}
- else if (w1 == "char_ip"_s)
+ else if (w1.data == "char_ip"_s)
{
- h = gethostbyname(w2.c_str());
+ h = gethostbyname(w2.data.c_str());
if (h != nullptr)
{
char_ip = IP4Address({
@@ -2848,81 +2849,81 @@ bool char_config(XString w1, ZString w2)
static_cast<uint8_t>(h->h_addr[3]),
});
PRINTF("Character server IP address : %s -> %s\n"_fmt,
- w2, char_ip);
+ w2.data, char_ip);
}
else
{
- PRINTF("Bad IP value: %s\n"_fmt, w2);
+ PRINTF("Bad IP value: %s\n"_fmt, w2.data);
return false;
}
}
- else if (w1 == "char_port"_s)
+ else if (w1.data == "char_port"_s)
{
- char_port = atoi(w2.c_str());
+ char_port = atoi(w2.data.c_str());
}
- else if (w1 == "char_txt"_s)
+ else if (w1.data == "char_txt"_s)
{
- char_txt = w2;
+ char_txt = w2.data;
}
- else if (w1 == "max_connect_user"_s)
+ else if (w1.data == "max_connect_user"_s)
{
- max_connect_user = atoi(w2.c_str());
+ max_connect_user = atoi(w2.data.c_str());
if (max_connect_user < 0)
max_connect_user = 0; // unlimited online players
}
- else if (w1 == "autosave_time"_s)
+ else if (w1.data == "autosave_time"_s)
{
- autosave_time = std::chrono::seconds(atoi(w2.c_str()));
+ autosave_time = std::chrono::seconds(atoi(w2.data.c_str()));
if (autosave_time <= std::chrono::seconds::zero())
autosave_time = DEFAULT_AUTOSAVE_INTERVAL;
}
- else if (w1 == "start_point"_s)
+ else if (w1.data == "start_point"_s)
{
- extract(w2, &start_point);
+ extract(w2.data, &start_point);
}
- else if (w1 == "unknown_char_name"_s)
+ else if (w1.data == "unknown_char_name"_s)
{
- unknown_char_name = stringish<CharName>(w2);
+ unknown_char_name = stringish<CharName>(w2.data);
}
- else if (w1 == "char_log_filename"_s)
+ else if (w1.data == "char_log_filename"_s)
{
- char_log_filename = w2;
+ char_log_filename = w2.data;
}
- else if (w1 == "char_name_letters"_s)
+ else if (w1.data == "char_name_letters"_s)
{
- if (!w2)
+ if (!w2.data)
char_name_letters.reset();
else
- for (uint8_t c : w2)
+ for (uint8_t c : w2.data)
char_name_letters[c] = true;
}
- else if (w1 == "online_txt_filename"_s)
+ else if (w1.data == "online_txt_filename"_s)
{
- online_txt_filename = w2;
+ online_txt_filename = w2.data;
}
- else if (w1 == "online_html_filename"_s)
+ else if (w1.data == "online_html_filename"_s)
{
- online_html_filename = w2;
+ online_html_filename = w2.data;
}
- else if (w1 == "online_gm_display_min_level"_s)
+ else if (w1.data == "online_gm_display_min_level"_s)
{
// minimum GM level to display 'GM' when we want to display it
- return extract(w2, &online_gm_display_min_level);
+ return extract(w2.data, &online_gm_display_min_level);
}
- else if (w1 == "online_refresh_html"_s)
+ else if (w1.data == "online_refresh_html"_s)
{
- online_refresh_html = atoi(w2.c_str());
+ online_refresh_html = atoi(w2.data.c_str());
if (online_refresh_html < 1)
online_refresh_html = 1;
}
- else if (w1 == "anti_freeze_enable"_s)
+ else if (w1.data == "anti_freeze_enable"_s)
{
- anti_freeze_enable = config_switch(w2);
+ anti_freeze_enable = config_switch(w2.data);
}
- else if (w1 == "anti_freeze_interval"_s)
+ else if (w1.data == "anti_freeze_interval"_s)
{
anti_freeze_interval = std::max(
- std::chrono::seconds(atoi(w2.c_str())),
+ std::chrono::seconds(atoi(w2.data.c_str())),
5_s);
}
else
@@ -2954,15 +2955,19 @@ void term_func(void)
}
static
-bool char_confs(XString key, ZString value)
+bool char_confs(io::Spanned<XString> key, io::Spanned<ZString> value)
{
- unsigned sum = 0;
- sum += char_config(key, value);
- sum += char_lan_config(key, value);
- sum += inter_config(key, value);
- if (sum >= 2)
- abort();
- return sum;
+ bool ok;
+ ok = char_config(key, value);
+ if (!ok)
+ return ok;
+ ok = char_lan_config(key, value);
+ if (!ok)
+ return ok;
+ ok = inter_config(key, value);
+ if (!ok)
+ return ok;
+ return ok;
}
int do_init(Slice<ZString> argv)
diff --git a/src/char/inter.cpp b/src/char/inter.cpp
index 4b10dbc..df05434 100644
--- a/src/char/inter.cpp
+++ b/src/char/inter.cpp
@@ -37,8 +37,11 @@
#include "../io/extract.hpp"
#include "../io/lock.hpp"
#include "../io/read.hpp"
+#include "../io/span.hpp"
#include "../io/write.hpp"
+#include "../mmo/config_parse.hpp"
+
#include "../proto2/char-map.hpp"
#include "../high/extract_mmo.hpp"
@@ -157,24 +160,24 @@ int inter_accreg_save(void)
return 0;
}
-bool inter_config(XString w1, ZString w2)
+bool inter_config(io::Spanned<XString> w1, io::Spanned<ZString> w2)
{
{
- if (w1 == "storage_txt"_s)
+ if (w1.data == "storage_txt"_s)
{
- storage_txt = w2;
+ storage_txt = w2.data;
}
- else if (w1 == "party_txt"_s)
+ else if (w1.data == "party_txt"_s)
{
- party_txt = w2;
+ party_txt = w2.data;
}
- else if (w1 == "accreg_txt"_s)
+ else if (w1.data == "accreg_txt"_s)
{
- accreg_txt = w2;
+ accreg_txt = w2.data;
}
- else if (w1 == "party_share_level"_s)
+ else if (w1.data == "party_share_level"_s)
{
- party_share_level = atoi(w2.c_str());
+ party_share_level = atoi(w2.data.c_str());
if (party_share_level < 0)
party_share_level = 0;
}
diff --git a/src/char/inter.hpp b/src/char/inter.hpp
index 31d80b1..741bc8d 100644
--- a/src/char/inter.hpp
+++ b/src/char/inter.hpp
@@ -25,7 +25,7 @@
namespace tmwa
{
-bool inter_config(XString key, ZString value);
+bool inter_config(io::Spanned<XString> key, io::Spanned<ZString> value);
void inter_init2();
void inter_save(void);
RecvResult inter_parse_frommap(Session *ms, uint16_t packet_id);
diff --git a/src/io/span.cpp b/src/io/span.cpp
index f4752f0..6d116c7 100644
--- a/src/io/span.cpp
+++ b/src/io/span.cpp
@@ -31,6 +31,20 @@ namespace tmwa
{
namespace io
{
+ io::LineSpan Line::to_span() const
+ {
+ io::LineSpan rv;
+ rv.begin.text = this->text;
+ rv.begin.filename = this->filename;
+ rv.begin.line = this->line;
+ rv.begin.column = 1;
+ rv.end.text = this->text;
+ rv.end.filename = this->filename;
+ rv.end.line = this->line;
+ rv.end.column = this->text.size();
+ return rv;
+ }
+
AString Line::message_str(ZString cat, ZString msg) const
{
MString out;
diff --git a/src/io/span.hpp b/src/io/span.hpp
index e474a7a..9962b7c 100644
--- a/src/io/span.hpp
+++ b/src/io/span.hpp
@@ -46,6 +46,8 @@ namespace io
void note(ZString msg) const { message("note"_s, msg); }
void warning(ZString msg) const { message("warning"_s, msg); }
void error(ZString msg) const { message("error"_s, msg); }
+
+ LineSpan to_span() const;
};
// psst, don't tell anyone
diff --git a/src/login/login.cpp b/src/login/login.cpp
index fb90cbe..92e7636 100644
--- a/src/login/login.cpp
+++ b/src/login/login.cpp
@@ -48,6 +48,7 @@
#include "../io/extract.hpp"
#include "../io/lock.hpp"
#include "../io/read.hpp"
+#include "../io/span.hpp"
#include "../io/tty.hpp"
#include "../io/write.hpp"
@@ -3012,15 +3013,15 @@ void parse_login(Session *s)
// Reading Lan Support configuration
//----------------------------------
static
-bool login_lan_config(XString w1, ZString w2)
+bool login_lan_config(io::Spanned<XString> w1, io::Spanned<ZString> w2)
{
struct hostent *h = nullptr;
{
- if (w1 == "lan_char_ip"_s)
+ if (w1.data == "lan_char_ip"_s)
{
// Read Char-Server Lan IP Address
- h = gethostbyname(w2.c_str());
+ h = gethostbyname(w2.data.c_str());
if (h != nullptr)
{
lan_char_ip = IP4Address({
@@ -3032,17 +3033,17 @@ bool login_lan_config(XString w1, ZString w2)
}
else
{
- PRINTF("Bad IP value: %s\n"_fmt, w2);
+ PRINTF("Bad IP value: %s\n"_fmt, w2.data);
return false;
}
PRINTF("LAN IP of char-server: %s.\n"_fmt, lan_char_ip);
}
- else if (w1 == "subnet"_s /*backward compatibility*/
- || w1 == "lan_subnet"_s)
+ else if (w1.data == "subnet"_s /*backward compatibility*/
+ || w1.data == "lan_subnet"_s)
{
- if (!extract(w2, &lan_subnet))
+ if (!extract(w2.data, &lan_subnet))
{
- PRINTF("Bad IP mask: %s\n"_fmt, w2);
+ PRINTF("Bad IP mask: %s\n"_fmt, w2.data);
return false;
}
PRINTF("Sub-network of the char-server: %s.\n"_fmt,
@@ -3083,195 +3084,195 @@ bool lan_check()
// Reading general configuration file
//-----------------------------------
static
-bool login_config(XString w1, ZString w2)
+bool login_config(io::Spanned<XString> w1, io::Spanned<ZString> w2)
{
{
- if (w1 == "admin_state"_s)
+ if (w1.data == "admin_state"_s)
{
- admin_state = config_switch(w2);
+ admin_state = config_switch(w2.data);
}
- else if (w1 == "admin_pass"_s)
+ else if (w1.data == "admin_pass"_s)
{
- admin_pass = stringish<AccountPass>(w2);
+ admin_pass = stringish<AccountPass>(w2.data);
}
- else if (w1 == "ladminallowip"_s)
+ else if (w1.data == "ladminallowip"_s)
{
- if (w2 == "clear"_s)
+ if (w2.data == "clear"_s)
{
access_ladmin.clear();
}
else
{
// a.b.c.d/0.0.0.0 (canonically, 0.0.0.0/0) covers all
- if (w2 == "all"_s)
+ if (w2.data == "all"_s)
{
// reset all previous values
access_ladmin.clear();
// set to all
access_ladmin.push_back(IP4Mask());
}
- else if (w2
+ else if (w2.data
&& !(access_ladmin.size() == 1
&& access_ladmin.front().mask() == IP4Address()))
{
// don't add IP if already 'all'
IP4Mask n;
- if (!extract(w2, &n))
+ if (!extract(w2.data, &n))
{
- PRINTF("Bad IP mask: %s\n"_fmt, w2);
+ PRINTF("Bad IP mask: %s\n"_fmt, w2.data);
return false;
}
access_ladmin.push_back(n);
}
}
}
- else if (w1 == "gm_pass"_s)
+ else if (w1.data == "gm_pass"_s)
{
- gm_pass = w2;
+ gm_pass = w2.data;
}
- else if (w1 == "level_new_gm"_s)
+ else if (w1.data == "level_new_gm"_s)
{
- level_new_gm = GmLevel::from(static_cast<uint32_t>(atoi(w2.c_str())));
+ level_new_gm = GmLevel::from(static_cast<uint32_t>(atoi(w2.data.c_str())));
}
- else if (w1 == "new_account"_s)
+ else if (w1.data == "new_account"_s)
{
- new_account = config_switch(w2);
+ new_account = config_switch(w2.data);
}
- else if (w1 == "login_port"_s)
+ else if (w1.data == "login_port"_s)
{
- login_port = atoi(w2.c_str());
+ login_port = atoi(w2.data.c_str());
}
- else if (w1 == "account_filename"_s)
+ else if (w1.data == "account_filename"_s)
{
- account_filename = w2;
+ account_filename = w2.data;
}
- else if (w1 == "gm_account_filename"_s)
+ else if (w1.data == "gm_account_filename"_s)
{
- gm_account_filename = w2;
+ gm_account_filename = w2.data;
}
- else if (w1 == "gm_account_filename_check_timer"_s)
+ else if (w1.data == "gm_account_filename_check_timer"_s)
{
- gm_account_filename_check_timer = std::chrono::seconds(atoi(w2.c_str()));
+ gm_account_filename_check_timer = std::chrono::seconds(atoi(w2.data.c_str()));
}
- else if (w1 == "login_log_filename"_s)
+ else if (w1.data == "login_log_filename"_s)
{
- login_log_filename = w2;
+ login_log_filename = w2.data;
}
- else if (w1 == "display_parse_login"_s)
+ else if (w1.data == "display_parse_login"_s)
{
- display_parse_login = config_switch(w2); // 0: no, 1: yes
+ display_parse_login = config_switch(w2.data); // 0: no, 1: yes
}
- else if (w1 == "display_parse_admin"_s)
+ else if (w1.data == "display_parse_admin"_s)
{
- display_parse_admin = config_switch(w2); // 0: no, 1: yes
+ display_parse_admin = config_switch(w2.data); // 0: no, 1: yes
}
- else if (w1 == "display_parse_fromchar"_s)
+ else if (w1.data == "display_parse_fromchar"_s)
{
- display_parse_fromchar = config_switch(w2); // 0: no, 1: yes (without packet 0x2714), 2: all packets
+ display_parse_fromchar = config_switch(w2.data); // 0: no, 1: yes (without packet 0x2714), 2: all packets
}
- else if (w1 == "min_level_to_connect"_s)
+ else if (w1.data == "min_level_to_connect"_s)
{
- min_level_to_connect = GmLevel::from(static_cast<uint32_t>(atoi(w2.c_str())));
+ min_level_to_connect = GmLevel::from(static_cast<uint32_t>(atoi(w2.data.c_str())));
}
- else if (w1 == "order"_s)
+ else if (w1.data == "order"_s)
{
- if (w2 == "deny,allow"_s || w2 == "deny, allow"_s)
+ if (w2.data == "deny,allow"_s || w2.data == "deny, allow"_s)
access_order = ACO::DENY_ALLOW;
- else if (w2 == "allow,deny"_s || w2 == "allow, deny"_s)
+ else if (w2.data == "allow,deny"_s || w2.data == "allow, deny"_s)
access_order = ACO::ALLOW_DENY;
- else if (w2 == "mutual-failture"_s || w2 == "mutual-failure"_s)
+ else if (w2.data == "mutual-failture"_s || w2.data == "mutual-failure"_s)
access_order = ACO::MUTUAL_FAILURE;
else
{
- PRINTF("Bad order: %s\n"_fmt, w2);
+ PRINTF("Bad order: %s\n"_fmt, w2.data);
return false;
}
}
- else if (w1 == "allow"_s)
+ else if (w1.data == "allow"_s)
{
- if (w2 == "clear"_s)
+ if (w2.data == "clear"_s)
{
access_allow.clear();
}
else
{
- if (w2 == "all"_s)
+ if (w2.data == "all"_s)
{
// reset all previous values
access_allow.clear();
// set to all
access_allow.push_back(IP4Mask());
}
- else if (w2
+ else if (w2.data
&& !(access_allow.size() == 1
&& access_allow.front().mask() == IP4Address()))
{
// don't add IP if already 'all'
IP4Mask n;
- if (!extract(w2, &n))
+ if (!extract(w2.data, &n))
{
- PRINTF("Bad IP mask: %s\n"_fmt, w2);
+ PRINTF("Bad IP mask: %s\n"_fmt, w2.data);
return false;
}
access_allow.push_back(n);
}
}
}
- else if (w1 == "deny"_s)
+ else if (w1.data == "deny"_s)
{
- if (w2 == "clear"_s)
+ if (w2.data == "clear"_s)
{
access_deny.clear();
}
else
{
- if (w2 == "all"_s)
+ if (w2.data == "all"_s)
{
// reset all previous values
access_deny.clear();
// set to all
access_deny.push_back(IP4Mask());
}
- else if (w2
+ else if (w2.data
&& !(access_deny.size() == 1
&& access_deny.front().mask() == IP4Address()))
{
// don't add IP if already 'all'
IP4Mask n;
- if (!extract(w2, &n))
+ if (!extract(w2.data, &n))
{
- PRINTF("Bad IP mask: %s\n"_fmt, w2);
+ PRINTF("Bad IP mask: %s\n"_fmt, w2.data);
return false;
}
access_deny.push_back(n);
}
}
}
- else if (w1 == "anti_freeze_enable"_s)
+ else if (w1.data == "anti_freeze_enable"_s)
{
- anti_freeze_enable = config_switch(w2);
+ anti_freeze_enable = config_switch(w2.data);
}
- else if (w1 == "anti_freeze_interval"_s)
+ else if (w1.data == "anti_freeze_interval"_s)
{
anti_freeze_interval = std::max(
- std::chrono::seconds(atoi(w2.c_str())),
+ std::chrono::seconds(atoi(w2.data.c_str())),
5_s);
}
- else if (w1 == "update_host"_s)
+ else if (w1.data == "update_host"_s)
{
- update_host = w2;
+ update_host = w2.data;
}
- else if (w1 == "main_server"_s)
+ else if (w1.data == "main_server"_s)
{
- main_server = stringish<ServerName>(w2);
+ main_server = stringish<ServerName>(w2.data);
}
- else if (w1 == "userid"_s)
+ else if (w1.data == "userid"_s)
{
- userid = stringish<AccountName>(w2);
+ userid = stringish<AccountName>(w2.data);
}
- else if (w1 == "passwd"_s)
+ else if (w1.data == "passwd"_s)
{
- passwd = stringish<AccountPass>(w2);
+ passwd = stringish<AccountPass>(w2.data);
}
else
{
@@ -3582,14 +3583,16 @@ void term_func(void)
}
static
-bool login_confs(XString key, ZString value)
+bool login_confs(io::Spanned<XString> key, io::Spanned<ZString> value)
{
- unsigned sum = 0;
- sum += login_config(key, value);
- sum += login_lan_config(key, value);
- if (sum >= 2)
- abort();
- return sum;
+ bool ok;
+ ok = login_config(key, value);
+ if (!ok)
+ return ok;
+ ok = login_lan_config(key, value);
+ if (!ok)
+ return ok;
+ return ok;
}
//------------------------------
diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp
index f08d561..5853dc2 100644
--- a/src/map/atcommand.cpp
+++ b/src/map/atcommand.cpp
@@ -351,43 +351,22 @@ Option<Borrowed<AtCommandInfo>> get_atcommandinfo_byname(XString name)
return atcommand_info.search(name);
}
-bool atcommand_config_read(ZString cfgName)
+static
+bool atcommand_config(io::Spanned<XString> w1, io::Spanned<ZString> w2)
{
- io::ReadFile in(cfgName);
- if (!in.is_open())
- {
- PRINTF("At commands configuration file not found: %s\n"_fmt, cfgName);
- return false;
- }
-
bool rv = true;
- AString line;
- while (in.getline(line))
{
- if (is_comment(line))
- continue;
- XString w1;
- ZString w2;
- if (!config_split(line, &w1, &w2))
- {
- PRINTF("Bad config line: %s\n"_fmt, line);
- rv = false;
- continue;
- }
- Option<P<AtCommandInfo>> p_ = get_atcommandinfo_byname(w1);
+ Option<P<AtCommandInfo>> p_ = get_atcommandinfo_byname(w1.data);
OMATCH_BEGIN (p_)
{
OMATCH_CASE_SOME (p)
{
- p->level = GmLevel::from(static_cast<uint32_t>(atoi(w2.c_str())));
+ p->level = GmLevel::from(static_cast<uint32_t>(atoi(w2.data.c_str())));
}
OMATCH_CASE_NONE ()
{
- if (w1 == "import"_s)
- rv &= atcommand_config_read(w2);
- else
{
- PRINTF("%s: bad line: %s\n"_fmt, cfgName, line);
+ w1.span.error("Unknown @command for permission level config."_s);
rv = false;
}
}
@@ -398,6 +377,11 @@ bool atcommand_config_read(ZString cfgName)
return rv;
}
+bool atcommand_config_read(ZString cfgName)
+{
+ return load_config_file(cfgName, atcommand_config);
+}
+
/// @ command processing functions
static
diff --git a/src/map/battle.cpp b/src/map/battle.cpp
index a04e402..983eac3 100644
--- a/src/map/battle.cpp
+++ b/src/map/battle.cpp
@@ -33,6 +33,7 @@
#include "../io/cxxstdio.hpp"
#include "../io/read.hpp"
+#include "../io/span.hpp"
#include "../mmo/config_parse.hpp"
#include "../mmo/cxxstdio_enums.hpp"
@@ -2278,18 +2279,9 @@ Battle_Config init_battle_config()
return battle_config;
}
-bool battle_config_read(ZString cfgName)
+static
+bool battle_config_(io::Spanned<XString> w1, io::Spanned<ZString> w2)
{
- bool rv = true;
- io::ReadFile in(cfgName);
- if (!in.is_open())
- {
- PRINTF("file not found: %s\n"_fmt, cfgName);
- return false;
- }
-
- AString line;
- while (in.getline(line))
{
#define BATTLE_CONFIG_VAR(name) {#name##_s, &battle_config.name}
const struct
@@ -2395,38 +2387,24 @@ bool battle_config_read(ZString cfgName)
BATTLE_CONFIG_VAR(mob_splash_radius),
};
- if (is_comment(line))
- continue;
- XString w1;
- ZString w2;
- if (!config_split(line, &w1, &w2))
- {
- PRINTF("Bad config line: %s\n"_fmt, line);
- rv = false;
- continue;
- }
-
- if (w1 == "import"_s)
- {
- battle_config_read(w2);
- continue;
- }
-
for (auto datum : data)
- if (w1 == datum.str)
+ {
+ if (w1.data == datum.str)
{
- *datum.val = config_switch(w2);
- goto continue_outer;
+ *datum.val = config_switch(w2.data);
+ return true;
}
+ }
- PRINTF("WARNING: unknown battle conf key: %s\n"_fmt, AString(w1));
- rv = false;
+ PRINTF("WARNING: unknown battle conf key: %s\n"_fmt, AString(w1.data));
+ return false;
- continue_outer:
- ;
}
+}
- return rv;
+bool battle_config_read(ZString cfgName)
+{
+ return load_config_file(cfgName, battle_config_);
}
void battle_config_check()
diff --git a/src/map/map.cpp b/src/map/map.cpp
index 527d3c3..25933a9 100644
--- a/src/map/map.cpp
+++ b/src/map/map.cpp
@@ -47,6 +47,7 @@
#include "../io/cxxstdio.hpp"
#include "../io/extract.hpp"
#include "../io/read.hpp"
+#include "../io/span.hpp"
#include "../io/tty.hpp"
#include "../io/write.hpp"
@@ -1477,49 +1478,25 @@ void map_log(XString line)
log_with_timestamp(*map_logfile, line);
}
-/*==========================================
- * 設定ファイルを読み込む
- *------------------------------------------
- */
static
-bool map_config_read(ZString cfgName)
+bool map_config(io::Spanned<XString> w1, io::Spanned<ZString> w2)
{
struct hostent *h = nullptr;
- io::ReadFile in(cfgName);
- if (!in.is_open())
- {
- PRINTF("Map configuration file not found at: %s\n"_fmt, cfgName);
- return false;
- }
-
- bool rv = true;
- AString line;
- while (in.getline(line))
{
- if (is_comment(line))
- continue;
- XString w1;
- ZString w2;
- if (!config_split(line, &w1, &w2))
+ if (w1.data == "userid"_s)
{
- PRINTF("Bad config line: %s\n"_fmt, line);
- rv = false;
- continue;
- }
- if (w1 == "userid"_s)
- {
- AccountName name = stringish<AccountName>(w2);
+ AccountName name = stringish<AccountName>(w2.data);
chrif_setuserid(name);
}
- else if (w1 == "passwd"_s)
+ else if (w1.data == "passwd"_s)
{
- AccountPass pass = stringish<AccountPass>(w2);
+ AccountPass pass = stringish<AccountPass>(w2.data);
chrif_setpasswd(pass);
}
- else if (w1 == "char_ip"_s)
+ else if (w1.data == "char_ip"_s)
{
- h = gethostbyname(w2.c_str());
+ h = gethostbyname(w2.data.c_str());
IP4Address w2ip;
if (h != nullptr)
{
@@ -1530,22 +1507,22 @@ bool map_config_read(ZString cfgName)
static_cast<uint8_t>(h->h_addr[3]),
});
PRINTF("Character server IP address : %s -> %s\n"_fmt,
- w2, w2ip);
+ w2.data, w2ip);
}
else
{
- PRINTF("Bad IP value: %s\n"_fmt, line);
+ PRINTF("Bad IP value: %s\n"_fmt, w2.data);
return false;
}
chrif_setip(w2ip);
}
- else if (w1 == "char_port"_s)
+ else if (w1.data == "char_port"_s)
{
- chrif_setport(atoi(w2.c_str()));
+ chrif_setport(atoi(w2.data.c_str()));
}
- else if (w1 == "map_ip"_s)
+ else if (w1.data == "map_ip"_s)
{
- h = gethostbyname(w2.c_str());
+ h = gethostbyname(w2.data.c_str());
IP4Address w2ip;
if (h != nullptr)
{
@@ -1556,66 +1533,70 @@ bool map_config_read(ZString cfgName)
static_cast<uint8_t>(h->h_addr[3]),
});
PRINTF("Map server IP address : %s -> %s\n"_fmt,
- w2, w2ip);
+ w2.data, w2ip);
}
else
{
- PRINTF("Bad IP value: %s\n"_fmt, line);
+ PRINTF("Bad IP value: %s\n"_fmt, w2.data);
return false;
}
clif_setip(w2ip);
}
- else if (w1 == "map_port"_s)
+ else if (w1.data == "map_port"_s)
{
- clif_setport(atoi(w2.c_str()));
+ clif_setport(atoi(w2.data.c_str()));
}
- else if (w1 == "map"_s)
+ else if (w1.data == "map"_s)
{
- MapName name = VString<15>(w2);
+ MapName name = VString<15>(w2.data);
map_addmap(name);
}
- else if (w1 == "delmap"_s)
+ else if (w1.data == "delmap"_s)
{
- MapName name = VString<15>(w2);
+ MapName name = VString<15>(w2.data);
map_delmap(name);
}
- else if (w1 == "npc"_s)
+ else if (w1.data == "npc"_s)
{
- npc_addsrcfile(w2);
+ npc_addsrcfile(w2.data);
}
- else if (w1 == "delnpc"_s)
+ else if (w1.data == "delnpc"_s)
{
- npc_delsrcfile(w2);
+ npc_delsrcfile(w2.data);
}
- else if (w1 == "autosave_time"_s)
+ else if (w1.data == "autosave_time"_s)
{
- autosave_time = std::chrono::seconds(atoi(w2.c_str()));
+ autosave_time = std::chrono::seconds(atoi(w2.data.c_str()));
if (autosave_time <= interval_t::zero())
autosave_time = DEFAULT_AUTOSAVE_INTERVAL;
}
- else if (w1 == "motd_txt"_s)
+ else if (w1.data == "motd_txt"_s)
{
- motd_txt = w2;
+ motd_txt = w2.data;
}
- else if (w1 == "mapreg_txt"_s)
+ else if (w1.data == "mapreg_txt"_s)
{
- mapreg_txt = w2;
+ mapreg_txt = w2.data;
}
- else if (w1 == "gm_log"_s)
+ else if (w1.data == "gm_log"_s)
{
- gm_log = std::move(w2);
+ gm_log = std::move(w2.data);
}
- else if (w1 == "log_file"_s)
+ else if (w1.data == "log_file"_s)
{
- map_set_logfile(w2);
+ map_set_logfile(w2.data);
}
- else if (w1 == "import"_s)
+ else if (w1.data == "import"_s)
{
- rv &= map_config_read(w2);
+ return load_config_file(w2.data, map_config);
+ }
+ else
+ {
+ return false;
}
}
- return rv;
+ return true;
}
static
@@ -1682,31 +1663,31 @@ int compare_item(Item *a, Item *b)
}
static
-bool map_confs(XString key, ZString value)
-{
- if (key == "map_conf"_s)
- return map_config_read(value);
- if (key == "battle_conf"_s)
- return battle_config_read(value);
- if (key == "atcommand_conf"_s)
- return atcommand_config_read(value);
-
- if (key == "item_db"_s)
- return itemdb_readdb(value);
- if (key == "mob_db"_s)
- return mob_readdb(value);
- if (key == "mob_skill_db"_s)
- return mob_readskilldb(value);
- if (key == "skill_db"_s)
- return skill_readdb(value);
- if (key == "magic_conf"_s)
- return magic::load_magic_file_v2(value);
-
- if (key == "resnametable"_s)
- return load_resnametable(value);
- if (key == "const_db"_s)
- return read_constdb(value);
- PRINTF("unknown map conf key: %s\n"_fmt, AString(key));
+bool map_confs(io::Spanned<XString> key, io::Spanned<ZString> value)
+{
+ if (key.data == "map_conf"_s)
+ return load_config_file(value.data, map_config);
+ if (key.data == "battle_conf"_s)
+ return battle_config_read(value.data);
+ if (key.data == "atcommand_conf"_s)
+ return atcommand_config_read(value.data);
+
+ if (key.data == "item_db"_s)
+ return itemdb_readdb(value.data);
+ if (key.data == "mob_db"_s)
+ return mob_readdb(value.data);
+ if (key.data == "mob_skill_db"_s)
+ return mob_readskilldb(value.data);
+ if (key.data == "skill_db"_s)
+ return skill_readdb(value.data);
+ if (key.data == "magic_conf"_s)
+ return magic::load_magic_file_v2(value.data);
+
+ if (key.data == "resnametable"_s)
+ return load_resnametable(value.data);
+ if (key.data == "const_db"_s)
+ return read_constdb(value.data);
+ PRINTF("unknown map conf key: %s\n"_fmt, AString(key.data));
return false;
}
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
}
diff --git a/src/mmo/config_parse.hpp b/src/mmo/config_parse.hpp
index db097e9..06432ba 100644
--- a/src/mmo/config_parse.hpp
+++ b/src/mmo/config_parse.hpp
@@ -23,11 +23,10 @@
namespace tmwa
{
-typedef bool (*ConfigItemParser)(XString key, ZString value);
+using ConfigItemParser = bool(io::Spanned<XString> key, io::Spanned<ZString> value);
bool is_comment(XString line);
-bool config_split(ZString line, XString *key, ZString *value);
-bool config_split(XString line, XString *key, XString *value);
+bool config_split(io::Spanned<ZString> line, io::Spanned<XString> *key, io::Spanned<ZString> *value);
/// Master config parser. This handles 'import' and 'version-ge' etc.
/// Then it defers to the inferior parser for a line it does not understand.
diff --git a/src/mmo/config_parse_test.cpp b/src/mmo/config_parse_test.cpp
new file mode 100644
index 0000000..e1170cb
--- /dev/null
+++ b/src/mmo/config_parse_test.cpp
@@ -0,0 +1,60 @@
+#include "config_parse.hpp"
+// config_parse_test.cpp - Testsuite for config parsers
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include <gtest/gtest.h>
+
+#include "../strings/literal.hpp"
+#include "../strings/rstring.hpp"
+
+#include "../io/span.hpp"
+
+#include "../poison.hpp"
+
+
+namespace tmwa
+{
+#define EXPECT_SPAN(span, bl,bc, el,ec) \
+ ({ \
+ EXPECT_EQ((span).begin.line, bl); \
+ EXPECT_EQ((span).begin.column, bc); \
+ EXPECT_EQ((span).end.line, el); \
+ EXPECT_EQ((span).end.column, ec); \
+ })
+TEST(configparse, keyvalue)
+{
+ // 0 1 2 3
+ // 123456789012345678901234567890
+ RString data = " key : value "_s;
+
+ io::Spanned<ZString> input, value;
+ io::Spanned<XString> key;
+ input.data = data;
+ input.span.begin.text = data;
+ input.span.begin.filename = "<config parse key/value test>"_s;
+ input.span.begin.line = 1;
+ input.span.begin.column = 1;
+ input.span.end = input.span.begin;
+ input.span.end.column = data.size();
+ EXPECT_EQ(data.size(), 30);
+ ASSERT_TRUE(config_split(input, &key, &value));
+ EXPECT_SPAN(key.span, 1,3, 1,5);
+ EXPECT_SPAN(value.span, 1,18, 1,30);
+}
+} // namespace tmwa
diff --git a/src/mmo/version.cpp b/src/mmo/version.cpp
index 93ea794..f91b748 100644
--- a/src/mmo/version.cpp
+++ b/src/mmo/version.cpp
@@ -69,8 +69,28 @@ LString CURRENT_VERSION_STRING = VERSION_STRING;
bool impl_extract(XString str, Version *vers)
{
*vers = {};
- // TODO should I try to extract dev and vend also?
- // It would've been useful during the magic migration.
+ // versions look like:
+ // 1.2.3 (release)
+ // 1.2.3+5 (vendor patches)
+ // 1.2.3-4 (dev patches)
+ // 1.2.3-4+5 (dev patches + vendor patches)
+ XString a, b;
+ if (extract(str, record<'+'>(&a, &b)))
+ {
+ if (!extract(b, &vers->vend))
+ {
+ return false;
+ }
+ str = a;
+ }
+ if (extract(str, record<'-'>(&a, &b)))
+ {
+ if (!extract(b, &vers->devel))
+ {
+ return false;
+ }
+ str = a;
+ }
return extract(str, record<'.'>(&vers->major, &vers->minor, &vers->patch));
}
diff --git a/src/monitor/main.cpp b/src/monitor/main.cpp
index c27cf77..75bf827 100644
--- a/src/monitor/main.cpp
+++ b/src/monitor/main.cpp
@@ -34,7 +34,7 @@
#include "../io/cxxstdio.hpp"
#include "../io/fd.hpp"
-#include "../io/read.hpp"
+#include "../io/line.hpp"
#include "../mmo/config_parse.hpp"
@@ -75,20 +75,19 @@ AString make_path(XString base, XString path)
}
static
-bool parse_option(XString name, ZString value)
+bool parse_option(io::Spanned<XString> name, io::Spanned<ZString> value)
{
- if (name == "login_server"_s)
- login_server = value;
- else if (name == "map_server"_s)
- map_server = value;
- else if (name == "char_server"_s)
- char_server = value;
- else if (name == "workdir"_s)
- workdir = value;
+ if (name.data == "login_server"_s)
+ login_server = value.data;
+ else if (name.data == "map_server"_s)
+ map_server = value.data;
+ else if (name.data == "char_server"_s)
+ char_server = value.data;
+ else if (name.data == "workdir"_s)
+ workdir = value.data;
else
{
- FPRINTF(stderr, "WARNING: ingnoring invalid option '%s' : '%s'\n"_fmt,
- AString(name), value);
+ name.span.error("invalid option key"_s);
return false;
}
return true;
@@ -98,30 +97,30 @@ static
bool read_config(ZString filename)
{
bool rv = true;
- io::ReadFile in(filename);
+ io::LineReader in(filename);
if (!in.is_open())
{
FPRINTF(stderr, "Monitor config file not found: %s\n"_fmt, filename);
exit(1);
}
- AString line;
- while (in.getline(line))
+ io::Line line_;
+ while (in.read_line(line_))
{
- if (is_comment(line))
+ io::Spanned<ZString> line = respan(line_.to_span(), ZString(line_.text));
+ if (is_comment(line.data))
continue;
- XString name;
- ZString value;
+ io::Spanned<XString> name;
+ io::Spanned<ZString> value;
if (!config_split(line, &name, &value))
{
- PRINTF("Bad line: %s\n"_fmt, line);
+ line.span.error("Unable to split config key: value"_s);
rv = false;
continue;
}
if (!parse_option(name, value))
{
- PRINTF("Bad key/value: %s\n"_fmt, line);
rv = false;
continue;
}
@@ -186,7 +185,10 @@ int main(int argc, char *argv[])
ZString config = CONFIG;
if (argc > 1)
config = ZString(strings::really_construct_from_a_pointer, argv[1], nullptr);
- read_config(config);
+ if (!read_config(config))
+ {
+ exit(1);
+ }
if (chdir(workdir.c_str()) < 0)
{