From 2cd52ab17ee1b830bc53321b112411122dddc1c8 Mon Sep 17 00:00:00 2001 From: Ben Longbons Date: Tue, 6 Jan 2015 17:31:21 -0800 Subject: Use Spanned while parsing config --- src/admin/ladmin.cpp | 23 ++--- src/char/char.cpp | 131 +++++++++++++------------- src/char/inter.cpp | 21 +++-- src/char/inter.hpp | 2 +- src/io/span.cpp | 14 +++ src/io/span.hpp | 2 + src/login/login.cpp | 161 ++++++++++++++++---------------- src/map/atcommand.cpp | 36 ++------ src/map/battle.cpp | 50 +++------- src/map/map.cpp | 155 ++++++++++++++----------------- src/mmo/config_parse.cpp | 210 ++++++++++++++++++++++++++++++++++-------- src/mmo/config_parse.hpp | 5 +- src/mmo/config_parse_test.cpp | 60 ++++++++++++ src/mmo/version.cpp | 24 ++++- src/monitor/main.cpp | 44 ++++----- 15 files changed, 564 insertions(+), 374 deletions(-) create mode 100644 src/mmo/config_parse_test.cpp 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 w1, io::Spanned 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(h->h_addr[0]), static_cast(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(w2); + admin_pass = stringish(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 w1, io::Spanned 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 w1, io::Spanned w2) { struct hostent *h = nullptr; { - if (w1 == "userid"_s) - userid = stringish(w2); - else if (w1 == "passwd"_s) - passwd = stringish(w2); - else if (w1 == "server_name"_s) + if (w1.data == "userid"_s) + userid = stringish(w2.data); + else if (w1.data == "passwd"_s) + passwd = stringish(w2.data); + else if (w1.data == "server_name"_s) { - server_name = stringish(w2); - PRINTF("%s server has been intialized\n"_fmt, w2); + server_name = stringish(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(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(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(w2); + unknown_char_name = stringish(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 key, io::Spanned 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 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 w1, io::Spanned 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 key, io::Spanned 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 w1, io::Spanned 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 w1, io::Spanned 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(w2); + admin_pass = stringish(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(atoi(w2.c_str()))); + level_new_gm = GmLevel::from(static_cast(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(atoi(w2.c_str()))); + min_level_to_connect = GmLevel::from(static_cast(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(w2); + main_server = stringish(w2.data); } - else if (w1 == "userid"_s) + else if (w1.data == "userid"_s) { - userid = stringish(w2); + userid = stringish(w2.data); } - else if (w1 == "passwd"_s) + else if (w1.data == "passwd"_s) { - passwd = stringish(w2); + passwd = stringish(w2.data); } else { @@ -3582,14 +3583,16 @@ void term_func(void) } static -bool login_confs(XString key, ZString value) +bool login_confs(io::Spanned key, io::Spanned 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> get_atcommandinfo_byname(XString name) return atcommand_info.search(name); } -bool atcommand_config_read(ZString cfgName) +static +bool atcommand_config(io::Spanned w1, io::Spanned 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_ = get_atcommandinfo_byname(w1); + Option> p_ = get_atcommandinfo_byname(w1.data); OMATCH_BEGIN (p_) { OMATCH_CASE_SOME (p) { - p->level = GmLevel::from(static_cast(atoi(w2.c_str()))); + p->level = GmLevel::from(static_cast(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 w1, io::Spanned 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 w1, io::Spanned 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(w2); + AccountName name = stringish(w2.data); chrif_setuserid(name); } - else if (w1 == "passwd"_s) + else if (w1.data == "passwd"_s) { - AccountPass pass = stringish(w2); + AccountPass pass = stringish(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(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(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 key, io::Spanned 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" @@ -39,40 +41,139 @@ bool is_comment(XString line) return not line or line.startswith("//"_s); } +template +static +bool do_split(io::Spanned line, io::Spanned *key, io::Spanned *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 +static +io::Spanned do_lstrip(io::Spanned value) +{ + io::Spanned 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 do_rstrip(io::Spanned value) +{ + io::Spanned 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 do_strip(io::Spanned value) +{ + return do_lstrip(do_rstrip(value)); +} + template inline -bool config_split_impl(ZS line, XString *key, ZS *value) +bool config_split_impl(io::Spanned line, io::Spanned *key, io::Spanned *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 line, io::Spanned *key, io::Spanned *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 avers, Borrowed 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 key; + io::Spanned 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 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 key, io::Spanned 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 line, io::Spanned *key, io::Spanned *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 +// +// 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 . + +#include + +#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 input, value; + io::Spanned key; + input.data = data; + input.span.begin.text = data; + input.span.begin.filename = ""_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 name, io::Spanned 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 line = respan(line_.to_span(), ZString(line_.text)); + if (is_comment(line.data)) continue; - XString name; - ZString value; + io::Spanned name; + io::Spanned 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) { -- cgit v1.2.3-60-g2f50