From 3b98f3439e33b15bba2036c402f9925340fdb2b9 Mon Sep 17 00:00:00 2001 From: Ben Longbons Date: Sat, 29 Jun 2013 23:23:43 -0700 Subject: Poison std::string and use the various string classes --- .gitignore | 2 + .travis.yml | 2 +- Makefile | 21 +- make.defs | 10 +- src/char/char.cpp | 430 ++++---- src/char/char.hpp | 8 +- src/char/int_party.cpp | 182 ++-- src/char/int_party.hpp | 4 +- src/char/int_storage.cpp | 26 +- src/char/int_storage.hpp | 4 +- src/char/inter.cpp | 99 +- src/char/inter.hpp | 8 +- src/common/const_array.hpp | 51 +- src/common/core.cpp | 6 +- src/common/core.hpp | 4 +- src/common/cxxstdio.cpp | 33 - src/common/cxxstdio.hpp | 96 +- src/common/cxxstdio_test.cpp | 3 + src/common/dumb_ptr.hpp | 137 +-- src/common/extract.cpp | 30 +- src/common/extract.hpp | 123 ++- src/common/extract_test.cpp | 319 ++++++ src/common/human_time_diff.hpp | 85 ++ src/common/human_time_diff_test.cpp | 84 ++ src/common/intern-pool.hpp | 13 +- src/common/intern-pool_test.cpp | 18 + src/common/io.hpp | 26 + src/common/lock.cpp | 20 +- src/common/lock.hpp | 6 +- src/common/md5calc.cpp | 92 +- src/common/md5calc.hpp | 37 +- src/common/md5calc_test.cpp | 27 + src/common/mmo.hpp | 121 ++- src/common/sanity.hpp | 23 - src/common/socket.cpp | 47 +- src/common/socket.hpp | 53 +- src/common/strings.hpp | 695 +++++++++++-- src/common/strings2_test.cpp | 118 +++ src/common/strings_test.cpp | 162 ++- src/common/timer.cpp | 4 +- src/common/timer.hpp | 4 +- src/common/utils.cpp | 131 ++- src/common/utils.hpp | 39 +- src/common/utils2.hpp | 29 + src/ladmin/ladmin.cpp | 1809 +++++++++------------------------- src/login/login.cpp | 848 +++++++--------- src/map/atcommand.cpp | 1751 +++++++++++++++----------------- src/map/atcommand.hpp | 8 +- src/map/battle.cpp | 338 +++---- src/map/battle.hpp | 2 +- src/map/chrif.cpp | 95 +- src/map/chrif.hpp | 20 +- src/map/clif.cpp | 309 +++--- src/map/clif.hpp | 48 +- src/map/grfio.cpp | 29 +- src/map/grfio.hpp | 5 +- src/map/intif.cpp | 76 +- src/map/intif.hpp | 12 +- src/map/itemdb.cpp | 121 +-- src/map/itemdb.hpp | 6 +- src/map/magic-expr-eval.hpp | 4 +- src/map/magic-expr.cpp | 270 +++-- src/map/magic-expr.hpp | 24 +- src/map/magic-interpreter-base.cpp | 23 +- src/map/magic-interpreter-parser.ypp | 63 +- src/map/magic-interpreter.hpp | 30 +- src/map/magic-stmt.cpp | 124 +-- src/map/magic.cpp | 22 +- src/map/magic.hpp | 10 +- src/map/map.cpp | 189 ++-- src/map/map.hpp | 100 +- src/map/map.t.hpp | 5 + src/map/mob.cpp | 379 +++---- src/map/mob.hpp | 20 +- src/map/npc.cpp | 571 +++++------ src/map/npc.hpp | 29 +- src/map/party.cpp | 40 +- src/map/party.hpp | 17 +- src/map/pc.cpp | 231 +++-- src/map/pc.hpp | 22 +- src/map/script.cpp | 532 +++++----- src/map/script.hpp | 45 +- src/map/skill.cpp | 142 +-- src/map/skill.hpp | 5 +- src/map/tmw.cpp | 80 +- src/map/tmw.hpp | 5 +- src/poison.hpp | 9 + src/tool/eathena-monitor.cpp | 49 +- src/warnings.hpp | 12 +- 89 files changed, 5860 insertions(+), 6101 deletions(-) delete mode 100644 src/common/cxxstdio.cpp create mode 100644 src/common/cxxstdio_test.cpp create mode 100644 src/common/extract_test.cpp create mode 100644 src/common/human_time_diff.hpp create mode 100644 src/common/human_time_diff_test.cpp create mode 100644 src/common/intern-pool_test.cpp create mode 100644 src/common/io.hpp create mode 100644 src/common/md5calc_test.cpp create mode 100644 src/common/strings2_test.cpp diff --git a/.gitignore b/.gitignore index 361fe2d..e27b0e0 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ /src/map/magic-interpreter-lexer.cpp # generated header dependencies /deps.make +# tags file +/tags diff --git a/.travis.yml b/.travis.yml index ead11f8..f45733d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,7 +41,7 @@ script: ## Do something after the main test script after_script: - - make test + - make test TESTER='valgrind --error-exitcode=1 --track-fds=yes' ### The rest of the file creates a build matrix ### containing gcc-4.6 through gcc-4.7 and clang-3.1 diff --git a/Makefile b/Makefile index 5436881..44b3fc2 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ all: ${PROGS} most: ${MOSTPROGS} clean: rm -rf ${PROGS} ${BUILD_DIR}/ -common: ${BUILD_DIR}/common/core.o ${BUILD_DIR}/common/db.o ${BUILD_DIR}/common/lock.o ${BUILD_DIR}/common/md5calc.o ${BUILD_DIR}/common/random.o ${BUILD_DIR}/common/nullpo.o ${BUILD_DIR}/common/socket.o ${BUILD_DIR}/common/timer.o ${BUILD_DIR}/common/utils.o ${BUILD_DIR}/common/cxxstdio.o ${BUILD_DIR}/common/extract.o +common: ${BUILD_DIR}/common/core.o ${BUILD_DIR}/common/db.o ${BUILD_DIR}/common/lock.o ${BUILD_DIR}/common/md5calc.o ${BUILD_DIR}/common/random.o ${BUILD_DIR}/common/nullpo.o ${BUILD_DIR}/common/socket.o ${BUILD_DIR}/common/timer.o ${BUILD_DIR}/common/utils.o ${BUILD_DIR}/common/extract.o magic: ${BUILD_DIR}/map/magic-interpreter-lexer.o ${BUILD_DIR}/map/magic-interpreter-parser.o ${BUILD_DIR}/map/magic-expr.o ${BUILD_DIR}/map/magic-interpreter-base.o ${BUILD_DIR}/map/magic-stmt.o ${BUILD_DIR}/map/magic.o # Top level programs @@ -64,15 +64,16 @@ ladmin: ${BUILD_DIR}/ladmin/ladmin eathena-monitor: ${BUILD_DIR}/tool/eathena-monitor cp -f $< $@ -${BUILD_DIR}/tests/main: ${BUILD_DIR}/tests/main.o $(patsubst src/%.cpp,obj/%.o,$(wildcard src/*/*_test.cpp)) ${BUILD_DIR}/gtest-all.o +${BUILD_DIR}/tests/main: ${BUILD_DIR}/tests/main.o $(patsubst src/%.cpp,obj/%.o,$(wildcard src/*/*_test.cpp)) ${BUILD_DIR}/gtest-all.o \ + ${BUILD_DIR}/common/extract.o ${BUILD_DIR}/common/md5calc.o ${BUILD_DIR}/common/random.o test: ${BUILD_DIR}/tests/main - $< + ${TESTER} $< ${TEST_ARGS} # Executable dependencies - generated by hand -${BUILD_DIR}/char/char: ${BUILD_DIR}/char/char.o ${BUILD_DIR}/char/inter.o ${BUILD_DIR}/char/int_party.o ${BUILD_DIR}/char/int_storage.o ${BUILD_DIR}/common/core.o ${BUILD_DIR}/common/socket.o ${BUILD_DIR}/common/timer.o ${BUILD_DIR}/common/db.o ${BUILD_DIR}/common/lock.o ${BUILD_DIR}/common/random.o ${BUILD_DIR}/common/utils.o ${BUILD_DIR}/common/cxxstdio.o ${BUILD_DIR}/common/extract.o -${BUILD_DIR}/ladmin/ladmin: ${BUILD_DIR}/ladmin/ladmin.o ${BUILD_DIR}/common/md5calc.o ${BUILD_DIR}/common/core.o ${BUILD_DIR}/common/socket.o ${BUILD_DIR}/common/timer.o ${BUILD_DIR}/common/db.o ${BUILD_DIR}/common/random.o ${BUILD_DIR}/common/utils.o ${BUILD_DIR}/common/cxxstdio.o -${BUILD_DIR}/login/login: ${BUILD_DIR}/login/login.o ${BUILD_DIR}/common/core.o ${BUILD_DIR}/common/socket.o ${BUILD_DIR}/common/timer.o ${BUILD_DIR}/common/db.o ${BUILD_DIR}/common/lock.o ${BUILD_DIR}/common/random.o ${BUILD_DIR}/common/md5calc.o ${BUILD_DIR}/common/utils.o ${BUILD_DIR}/common/cxxstdio.o ${BUILD_DIR}/common/extract.o -${BUILD_DIR}/map/map: ${BUILD_DIR}/map/map.o ${BUILD_DIR}/map/tmw.o ${BUILD_DIR}/map/magic-interpreter-lexer.o ${BUILD_DIR}/map/magic-interpreter-parser.o ${BUILD_DIR}/map/magic-interpreter-base.o ${BUILD_DIR}/map/magic-expr.o ${BUILD_DIR}/map/magic-stmt.o ${BUILD_DIR}/map/magic.o ${BUILD_DIR}/map/map.o ${BUILD_DIR}/map/chrif.o ${BUILD_DIR}/map/clif.o ${BUILD_DIR}/map/pc.o ${BUILD_DIR}/map/npc.o ${BUILD_DIR}/map/path.o ${BUILD_DIR}/map/itemdb.o ${BUILD_DIR}/map/mob.o ${BUILD_DIR}/map/script.o ${BUILD_DIR}/map/storage.o ${BUILD_DIR}/map/skill.o ${BUILD_DIR}/map/skill-pools.o ${BUILD_DIR}/map/atcommand.o ${BUILD_DIR}/map/battle.o ${BUILD_DIR}/map/intif.o ${BUILD_DIR}/map/trade.o ${BUILD_DIR}/map/party.o ${BUILD_DIR}/common/core.o ${BUILD_DIR}/common/socket.o ${BUILD_DIR}/common/timer.o ${BUILD_DIR}/map/grfio.o ${BUILD_DIR}/common/db.o ${BUILD_DIR}/common/lock.o ${BUILD_DIR}/common/nullpo.o ${BUILD_DIR}/common/random.o ${BUILD_DIR}/common/md5calc.o ${BUILD_DIR}/common/utils.o ${BUILD_DIR}/common/cxxstdio.o ${BUILD_DIR}/common/extract.o +${BUILD_DIR}/char/char: ${BUILD_DIR}/char/char.o ${BUILD_DIR}/char/inter.o ${BUILD_DIR}/char/int_party.o ${BUILD_DIR}/char/int_storage.o ${BUILD_DIR}/common/core.o ${BUILD_DIR}/common/socket.o ${BUILD_DIR}/common/timer.o ${BUILD_DIR}/common/db.o ${BUILD_DIR}/common/lock.o ${BUILD_DIR}/common/random.o ${BUILD_DIR}/common/utils.o ${BUILD_DIR}/common/extract.o +${BUILD_DIR}/ladmin/ladmin: ${BUILD_DIR}/ladmin/ladmin.o ${BUILD_DIR}/common/md5calc.o ${BUILD_DIR}/common/core.o ${BUILD_DIR}/common/socket.o ${BUILD_DIR}/common/timer.o ${BUILD_DIR}/common/db.o ${BUILD_DIR}/common/extract.o ${BUILD_DIR}/common/random.o ${BUILD_DIR}/common/utils.o +${BUILD_DIR}/login/login: ${BUILD_DIR}/login/login.o ${BUILD_DIR}/common/core.o ${BUILD_DIR}/common/socket.o ${BUILD_DIR}/common/timer.o ${BUILD_DIR}/common/db.o ${BUILD_DIR}/common/lock.o ${BUILD_DIR}/common/random.o ${BUILD_DIR}/common/md5calc.o ${BUILD_DIR}/common/utils.o ${BUILD_DIR}/common/extract.o +${BUILD_DIR}/map/map: ${BUILD_DIR}/map/map.o ${BUILD_DIR}/map/tmw.o ${BUILD_DIR}/map/magic-interpreter-lexer.o ${BUILD_DIR}/map/magic-interpreter-parser.o ${BUILD_DIR}/map/magic-interpreter-base.o ${BUILD_DIR}/map/magic-expr.o ${BUILD_DIR}/map/magic-stmt.o ${BUILD_DIR}/map/magic.o ${BUILD_DIR}/map/map.o ${BUILD_DIR}/map/chrif.o ${BUILD_DIR}/map/clif.o ${BUILD_DIR}/map/pc.o ${BUILD_DIR}/map/npc.o ${BUILD_DIR}/map/path.o ${BUILD_DIR}/map/itemdb.o ${BUILD_DIR}/map/mob.o ${BUILD_DIR}/map/script.o ${BUILD_DIR}/map/storage.o ${BUILD_DIR}/map/skill.o ${BUILD_DIR}/map/skill-pools.o ${BUILD_DIR}/map/atcommand.o ${BUILD_DIR}/map/battle.o ${BUILD_DIR}/map/intif.o ${BUILD_DIR}/map/trade.o ${BUILD_DIR}/map/party.o ${BUILD_DIR}/common/core.o ${BUILD_DIR}/common/socket.o ${BUILD_DIR}/common/timer.o ${BUILD_DIR}/map/grfio.o ${BUILD_DIR}/common/db.o ${BUILD_DIR}/common/lock.o ${BUILD_DIR}/common/nullpo.o ${BUILD_DIR}/common/random.o ${BUILD_DIR}/common/md5calc.o ${BUILD_DIR}/common/utils.o ${BUILD_DIR}/common/extract.o ${BUILD_DIR}/tool/eathena-monitor: ${BUILD_DIR}/tool/eathena-monitor.o ${BUILD_DIR}/common/utils.o # silence build warnings for code beyond my control @@ -93,3 +94,9 @@ BACKUPS=numbered install: install -d ${prefix}/bin/ install --backup=${BACKUPS} -t ${prefix}/bin/ $(wildcard ${PROGS}) + +# might need changes later to handle static declarations (solution: +# run ctags twice, specifying forward patterns for the declarations +# and backward patterns for the definition) +tags: $(shell git ls-files src/) + ctags --totals --c-kinds=+px -f $@ $^ diff --git a/make.defs b/make.defs index b5b47fa..0f35244 100644 --- a/make.defs +++ b/make.defs @@ -5,7 +5,10 @@ CXX = g++ LEX = flex BISON = bison -CXXFLAGS = -pipe -g -O2 ${WARNINGS} +CXXFLAGS = ${DEBUG} ${OPT} ${WARNINGS} +# these being separate use useful for development +DEBUG = -g +OPT = -pipe -O2 WARNINGS = -include src/warnings.hpp # Location of gtest source tree. It must contain the file src/gtest-all.cc @@ -14,10 +17,11 @@ WARNINGS = -include src/warnings.hpp # http://code.google.com/p/googletest/wiki/FAQ#Why_is_it_not_recommended_to_install_a_pre-compiled_copy_of_Goog GTEST_DIR = /usr/src/gtest -# works on both x86 and x86_64 +# gdb bug 15801 +CXX += -fabi-version=6 override CXX += -std=c++0x # for linking override CC = ${CXX} override CXXFLAGS += -fstack-protector -fno-strict-aliasing - +override CXXFLAGS += -fvisibility=hidden diff --git a/src/char/char.cpp b/src/char/char.cpp index d7e7a41..6108dca 100644 --- a/src/char/char.cpp +++ b/src/char/char.cpp @@ -12,12 +12,15 @@ #include #include +#include #include #include "../common/core.hpp" #include "../common/cxxstdio.hpp" #include "../common/db.hpp" #include "../common/extract.hpp" +#include "../common/human_time_diff.hpp" +#include "../common/io.hpp" #include "../common/lock.hpp" #include "../common/socket.hpp" #include "../common/timer.hpp" @@ -48,21 +51,21 @@ std::chrono::milliseconds DEFAULT_AUTOSAVE_INTERVAL = static int login_fd, char_fd; static -char userid[24]; +AccountName userid; static -char passwd[24]; +AccountPass passwd; static -char server_name[20]; +ServerName server_name; static -char wisp_server_name[24] = "Server"; +CharName wisp_server_name = stringish("Server"); static -char login_ip_str[16]; +IP_String login_ip_str; static int login_ip; static int login_port = 6900; static -char char_ip_str[16]; +IP_String char_ip_str; static int char_ip; static @@ -72,31 +75,29 @@ int char_maintenance; static int char_new; static -char char_txt[1024]; +FString char_txt; static -char unknown_char_name[24] = "Unknown"; +CharName unknown_char_name = stringish("Unknown"); static -char char_log_filename[1024] = "log/char.log"; +FString char_log_filename = "log/char.log"; //Added for lan support static -char lan_map_ip[128]; +IP_String lan_map_ip; static uint8_t subneti[4]; static uint8_t subnetmaski[4]; static -int name_ignoring_case = 0; // Allow or not identical name for characters but with a different case by [Yor] -static int char_name_option = 0; // Option to know which letters/symbols are authorised in the name of a character (0: all, 1: only those in char_name_letters, 2: all EXCEPT those in char_name_letters) by [Yor] static -char char_name_letters[1024] = ""; // list of letters/symbols authorised (or not) in a character name. by [Yor] +std::bitset<256> char_name_letters; // list of letters/symbols authorised (or not) in a character name. by [Yor] struct char_session_data : SessionData { int account_id, login_id1, login_id2, sex; unsigned short packet_tmw_version; - char email[40]; // e-mail (default: a@a.com) by [Yor] - TimeT connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) + AccountEmail email; + TimeT connect_until_time; }; void SessionDeleter::operator()(SessionData *sd) @@ -140,16 +141,16 @@ int start_armor = 1202; // Initial position (it's possible to set it in conf file) static -struct point start_point = { "new_1-1.gat", 53, 111 }; +struct point start_point = { {"001-1.gat"}, 273, 354 }; static std::vector gm_accounts; // online players by [Yor] static -char online_txt_filename[1024] = "online.txt"; +FString online_txt_filename = "online.txt"; static -char online_html_filename[1024] = "online.html"; +FString online_html_filename = "online.html"; static int online_sorting_option = 0; // sorting option to display online players in online files static @@ -168,13 +169,13 @@ pid_t pid = 0; // For forked DB writes //------------------------------ // Writing function of logs file //------------------------------ -void char_log(const_string line) +void char_log(XString line) { - FILE *logfp = fopen_(char_log_filename, "a"); + FILE *logfp = fopen(char_log_filename.c_str(), "a"); if (!logfp) return; log_with_timestamp(logfp, line); - fclose_(logfp); + fclose(logfp); } //---------------------------------------------------------------------- @@ -198,26 +199,16 @@ int isGM(int account_id) // and returns index if only 1 character is found // and similar to the searched name. //---------------------------------------------- -const mmo_charstatus *search_character(const char *character_name) +const mmo_charstatus *search_character(CharName character_name) { - int quantity = 0; - const mmo_charstatus *index = nullptr; for (const mmo_charstatus& cd : char_data) { - // Without case sensitive check (increase the number of similar character names found) - if (strcasecmp(cd.name, character_name) == 0) { // Strict comparison (if found, we finish the function immediatly with correct value) - if (strcmp(cd.name, character_name) == 0) + if (cd.name == character_name) return &cd; - quantity++; - index = &cd; } } - // Here, the exact character name is not found - // We return the found index of a similar account ONLY if there is 1 similar character - if (quantity == 1) - return index; // Exact character name is not found and 0 or more than 1 similar characters have been found ==> we say not found return nullptr; @@ -226,18 +217,16 @@ const mmo_charstatus *search_character(const char *character_name) //------------------------------------------------- // Function to create the character line (for save) //------------------------------------------------- -__inline__ static -std::string mmo_char_tostr(struct mmo_charstatus *p) +static +FString mmo_char_tostr(struct mmo_charstatus *p) { // on multi-map server, sometimes it's posssible that last_point become void. (reason???) We check that to not lost character at restart. - if (p->last_point.map_[0] == '\0') + if (!p->last_point.map_) { - strzcpy(p->last_point.map_, "001-1.gat", 16); - p->last_point.x = 273; - p->last_point.y = 354; + p->last_point = start_point; } - std::string str_p; + MString str_p; str_p += STRPRINTF( "%d\t" "%d,%d\t" @@ -329,11 +318,11 @@ std::string mmo_char_tostr(struct mmo_charstatus *p) p->global_reg[i].value); str_p += '\t'; - return str_p; + return FString(str_p); } static -bool extract(const_string str, struct point *p) +bool extract(XString str, struct point *p) { return extract(str, record<','>(&p->map_, &p->x, &p->y)); } @@ -346,7 +335,7 @@ struct skill_loader }; static -bool extract(const_string str, struct skill_loader *s) +bool extract(XString str, struct skill_loader *s) { uint32_t flags_and_level; if (!extract(str, @@ -361,7 +350,7 @@ bool extract(const_string str, struct skill_loader *s) // Function to set the character from the line (at read of characters file) //------------------------------------------------------------------------- static -bool extract(const_string str, struct mmo_charstatus *p) +bool extract(XString str, struct mmo_charstatus *p) { // initilialise character *p = mmo_charstatus{}; @@ -397,14 +386,14 @@ bool extract(const_string str, struct mmo_charstatus *p) vrec<' '>(&vars)))) return false; - if (strcmp(wisp_server_name, p->name) == 0) + if (wisp_server_name == p->name) return false; for (const mmo_charstatus& cd : char_data) { if (cd.char_id == p->char_id) return false; - if (strcmp(cd.name, p->name) == 0) + if (cd.name == p->name) return false; } @@ -448,7 +437,7 @@ int mmo_char_init(void) char_data.clear(); online_chars.clear(); - std::ifstream in(char_txt); + std::ifstream in(char_txt.c_str()); if (!in.is_open()) { PRINTF("Characters file not found: %s.\n", char_txt); @@ -459,17 +448,13 @@ int mmo_char_init(void) } int line_count = 0; - std::string line; - while (std::getline(in, line)) + FString line; + while (io::getline(in, line)) { line_count++; - if (line[0] == '/' && line[1] == '/') + if (line.startswith("//")) continue; - if (line.back() == '\r') - { - line.back() = 0; - } { int i, j = 0; @@ -511,10 +496,7 @@ static void mmo_char_sync(void) { int lock; - FILE *fp; - - // Data save - fp = lock_fopen(char_txt, &lock); + FILE *fp = lock_fopen(char_txt, &lock); if (fp == NULL) { PRINTF("WARNING: Server can't not save characters.\n"); @@ -525,7 +507,7 @@ void mmo_char_sync(void) // yes, we need a mutable reference to do the saves ... for (mmo_charstatus& cd : char_data) { - std::string line = mmo_char_tostr(&cd); + FString line = mmo_char_tostr(&cd); fwrite(line.data(), 1, line.size(), fp); fputc('\n', fp); } @@ -565,43 +547,17 @@ void mmo_char_sync_timer(TimerData *, tick_t) _exit(0); } -//---------------------------------------------------- -// Remove trailing whitespace from a name -//---------------------------------------------------- -static -void remove_trailing_blanks(char *name) -{ - char *tail = name + strlen(name) - 1; - - while (tail > name && *tail == ' ') - *tail-- = 0; -} - -//---------------------------------------------------- -// Remove prefix whitespace from a name -//---------------------------------------------------- -static -void remove_prefix_blanks(char *name) -{ - char *dst = name; - char *src = name; - - while (*src == ' ') // find first nonblank - ++src; - while ((*dst++ = *src++)); // `strmove' -} - //----------------------------------- // Function to create a new character //----------------------------------- static -mmo_charstatus *make_new_char(int fd, char *name, const uint8_t (&stats)[6], uint8_t slot, uint16_t hair_color, uint16_t hair_style) +mmo_charstatus *make_new_char(int fd, CharName name, const uint8_t (&stats)[6], uint8_t slot, uint16_t hair_color, uint16_t hair_style) { // ugh char_session_data *sd = static_cast(session[fd]->session_data.get()); // remove control characters from the name - if (remove_control_chars(name)) + if (!name.to__actual().is_print()) { CHAR_LOG("Make new char error (control char received in the name): (connection #%d, account: %d).\n", fd, sd->account_id); @@ -609,11 +565,15 @@ mmo_charstatus *make_new_char(int fd, char *name, const uint8_t (&stats)[6], uin } // Eliminate whitespace - remove_trailing_blanks(name); - remove_prefix_blanks(name); + if (name.to__actual() != name.to__actual().strip()) + { + CHAR_LOG("Make new char error (leading/trailing whitespace): (connection #%d, account: %d, name: '%s'.\n", + fd, sd->account_id, name); + return nullptr; + } // check lenght of character name - if (strlen(name) < 4) + if (name.to__actual().size() < 4) { CHAR_LOG("Make new char error (character name too small): (connection #%d, account: %d, name: '%s').\n", fd, sd->account_id, name); @@ -624,22 +584,22 @@ mmo_charstatus *make_new_char(int fd, char *name, const uint8_t (&stats)[6], uin if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised - for (int i = 0; name[i]; i++) - if (strchr(char_name_letters, name[i]) == NULL) + for (uint8_t c : name.to__actual()) + if (!char_name_letters[c]) { CHAR_LOG("Make new char error (invalid letter in the name): (connection #%d, account: %d), name: %s, invalid letter: %c.\n", - fd, sd->account_id, name, name[i]); + fd, sd->account_id, name, c); return nullptr; } } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden - for (int i = 0; name[i]; i++) - if (strchr(char_name_letters, name[i]) != NULL) + for (uint8_t c : name.to__actual()) + if (char_name_letters[c]) { CHAR_LOG("Make new char error (invalid letter in the name): (connection #%d, account: %d), name: %s, invalid letter: %c.\n", - fd, sd->account_id, name, name[i]); + fd, sd->account_id, name, c); return nullptr; } } // else, all letters/symbols are authorised (except control char removed before) @@ -674,9 +634,7 @@ mmo_charstatus *make_new_char(int fd, char *name, const uint8_t (&stats)[6], uin for (const mmo_charstatus& cd : char_data) { - if ((name_ignoring_case != 0 && strcmp(cd.name, name) == 0) - || (name_ignoring_case == 0 - && strcasecmp(cd.name, name) == 0)) + if (cd.name == name) { CHAR_LOG("Make new char error (name already exists): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %s), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d.\n", fd, sd->account_id, slot, name, cd.name, @@ -697,7 +655,7 @@ mmo_charstatus *make_new_char(int fd, char *name, const uint8_t (&stats)[6], uin } } - if (strcmp(wisp_server_name, name) == 0) + if (wisp_server_name == name) { CHAR_LOG("Make new char error (name used is wisp name for server): (connection #%d, account: %d) slot %d, name: %s (actual name whisper server: %s), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d.\n", fd, sd->account_id, slot, name, wisp_server_name, @@ -723,7 +681,7 @@ mmo_charstatus *make_new_char(int fd, char *name, const uint8_t (&stats)[6], uin cd.char_id = char_id_count++; cd.account_id = sd->account_id; cd.char_num = slot; - strcpy(cd.name, name); + cd.name = name; cd.species = 0; cd.base_level = 1; cd.job_level = 1; @@ -771,10 +729,10 @@ static void create_online_files(void) { // write files - FILE *fp = fopen_(online_txt_filename, "w"); + FILE *fp = fopen(online_txt_filename.c_str(), "w"); if (fp != NULL) { - FILE *fp2 = fopen_(online_html_filename, "w"); + FILE *fp2 = fopen(online_html_filename.c_str(), "w"); if (fp2 != NULL) { // get time @@ -836,9 +794,9 @@ void create_online_files(void) FPRINTF(fp2, " "); if (gml >= online_gm_display_min_level) FPRINTF(fp2, ""); - for (int k = 0; cd.name[k]; k++) + for (char c : cd.name.to__actual()) { - switch (cd.name[k]) + switch (c) { case '&': FPRINTF(fp2, "&"); @@ -850,7 +808,7 @@ void create_online_files(void) FPRINTF(fp2, ">"); break; default: - FPRINTF(fp2, "%c", cd.name[k]); + FPRINTF(fp2, "%c", c); break; }; } @@ -882,9 +840,9 @@ void create_online_files(void) } FPRINTF(fp2, " \n"); FPRINTF(fp2, "\n"); - fclose_(fp2); + fclose(fp2); } - fclose_(fp); + fclose(fp); } return; @@ -983,7 +941,7 @@ int mmo_char_send006b(int fd, struct char_session_data *sd) WFIFOW(fd, j + 72) = find_equip_view(p, EPOS::MISC2); // WFIFOW(fd,j+72) = p->clothes_color; - WFIFO_STRING(fd, j + 74, p->name, 24); + WFIFO_STRING(fd, j + 74, p->name.to__actual(), 24); WFIFOB(fd, j + 98) = min(p->attrs[ATTR::STR], 255); WFIFOB(fd, j + 99) = min(p->attrs[ATTR::AGI], 255); @@ -1192,9 +1150,9 @@ void parse_tologin(int fd) // PRINTF("max_connect_user (unlimited) -> accepted.\n"); // else // PRINTF("count_users(): %d < max_connect_user (%d) -> accepted.\n", count_users(), max_connect_user); - RFIFO_STRING(fd, 7, sd->email, 40); - if (e_mail_check(sd->email) == 0) - strzcpy(sd->email, "a@a.com", 40); // default e-mail + sd->email = stringish(RFIFO_STRING<40>(fd, 7)); + if (!e_mail_check(sd->email)) + sd->email = DEFAULT_EMAIL; sd->connect_until_time = static_cast(RFIFOL(fd, 47)); // send characters to player mmo_char_send006b(i, sd); @@ -1226,9 +1184,9 @@ void parse_tologin(int fd) { if (sd->account_id == RFIFOL(fd, 2)) { - RFIFO_STRING(fd, 6, sd->email, 40); - if (e_mail_check(sd->email) == 0) - strzcpy(sd->email, "a@a.com", 40); // default e-mail + sd->email = stringish(RFIFO_STRING<40>(fd, 6)); + if (!e_mail_check(sd->email)) + sd->email = DEFAULT_EMAIL; sd->connect_until_time = static_cast(RFIFOL(fd, 46)); break; } @@ -1309,29 +1267,20 @@ void parse_tologin(int fd) else { size_t len = RFIFOL(fd, 4); - char message[len]; - RFIFO_STRING(fd, 8, message, len); - remove_control_chars(message); - // remove all first spaces - char *p = message; - while (p[0] == ' ') - p++; + FString message = RFIFO_STRING(fd, 8, len).to_print().lstrip(); // if message is only composed of spaces - if (p[0] == '\0') + if (!message) CHAR_LOG("Receiving a message for broadcast, but message is only a lot of spaces.\n"); // else send message to all map-servers else { - { - const char *message_ptr = message; - CHAR_LOG("'ladmin': Receiving a message for broadcast (message (in yellow): %s)\n", - message_ptr); - } + CHAR_LOG("'ladmin': Receiving a message for broadcast (message (in yellow): %s)\n", + message); // send broadcast to all map-servers uint8_t buf[4 + len]; WBUFW(buf, 0) = 0x3800; - WBUFW(buf, 2) = 4 + sizeof(message); - WBUF_STRING(buf, 4, message, sizeof(message)); + WBUFW(buf, 2) = 4 + len; + WBUF_STRING(buf, 4, message, len); mapif_sendall(buf, WBUFW(buf, 2)); } } @@ -1351,7 +1300,7 @@ void parse_tologin(int fd) p < RFIFOW(fd, 2) && j < ACCOUNT_REG2_NUM; p += 36, j++) { - RFIFO_STRING(fd, p, reg[j].str, 32); + reg[j].str = stringish(RFIFO_STRING<32>(fd, p)); reg[j].value = RFIFOL(fd, p + 32); } set_account_reg2(acc, j, reg); @@ -1594,7 +1543,6 @@ void parse_frommap(int fd) { // don't send request if no login-server WFIFOW(login_fd, 0) = 0x2709; WFIFOSET(login_fd, 2); -// PRINTF("char : request from map-server to reload GM accounts -> login-server.\n"); } RFIFOSKIP(fd, 2); break; @@ -1604,13 +1552,12 @@ void parse_frommap(int fd) if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd, 2)) return; { - for (char (&foo)[16] : server[id].maps) - strzcpy(foo, "", 16); + for (MapName &foo : server[id].maps) + foo = MapName(); int j = 0; for (int i = 4; i < RFIFOW(fd, 2); i += 16) { - RFIFO_STRING(fd, i, server[id].maps[j], 16); -// PRINTF("set map %d.%d : %s\n", id, j, server[id].map[j]); + server[id].maps[j] = RFIFO_STRING<16>(fd, i); j++; } { @@ -1624,7 +1571,7 @@ void parse_frommap(int fd) } WFIFOW(fd, 0) = 0x2afb; WFIFOB(fd, 2) = 0; - WFIFO_STRING(fd, 3, wisp_server_name, 24); + WFIFO_STRING(fd, 3, wisp_server_name.to__actual(), 24); WFIFOSET(fd, 27); { unsigned char buf[16384]; @@ -1831,7 +1778,7 @@ void parse_frommap(int fd) if (RFIFOREST(fd) < 6) return; { - const char *name = unknown_char_name; + CharName name = unknown_char_name; for (const mmo_charstatus& cd : char_data) { if (cd.char_id == RFIFOL(fd, 2)) @@ -1842,7 +1789,7 @@ void parse_frommap(int fd) } WFIFOW(fd, 0) = 0x2b09; WFIFOL(fd, 2) = RFIFOL(fd, 2); - WFIFO_STRING(fd, 6, name, 24); + WFIFO_STRING(fd, 6, name.to__actual(), 24); WFIFOSET(fd, 30); } RFIFOSKIP(fd, 6); @@ -1887,9 +1834,8 @@ void parse_frommap(int fd) if (RFIFOREST(fd) < 44) return; { - char character_name[24]; int acc = RFIFOL(fd, 2); // account_id of who ask (-1 if nobody) - RFIFO_STRING(fd, 6, character_name, 24); + CharName character_name = stringish(RFIFO_STRING<24>(fd, 6)); // prepare answer WFIFOW(fd, 0) = 0x2b0f; // answer WFIFOL(fd, 2) = acc; // who want do operation @@ -1898,7 +1844,7 @@ void parse_frommap(int fd) const mmo_charstatus *cd = search_character(character_name); if (cd) { - WFIFO_STRING(fd, 6, cd->name, 24); // put correct name if found + WFIFO_STRING(fd, 6, cd->name.to__actual(), 24); // put correct name if found WFIFOW(fd, 32) = 0; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline switch (RFIFOW(fd, 30)) { @@ -1928,12 +1874,9 @@ void parse_frommap(int fd) { // don't send request if no login-server WFIFOW(login_fd, 0) = 0x2725; WFIFOL(login_fd, 2) = cd->account_id; // account value - WFIFOW(login_fd, 6) = RFIFOW(fd, 32); // year - WFIFOW(login_fd, 8) = RFIFOW(fd, 34); // month - WFIFOW(login_fd, 10) = RFIFOW(fd, 36); // day - WFIFOW(login_fd, 12) = RFIFOW(fd, 38); // hour - WFIFOW(login_fd, 14) = RFIFOW(fd, 40); // minute - WFIFOW(login_fd, 16) = RFIFOW(fd, 42); // second + HumanTimeDiff ban_change; + RFIFO_STRUCT(fd, 32, ban_change); + WFIFO_STRUCT(login_fd, 6, ban_change); WFIFOSET(login_fd, 18); // PRINTF("char : status -> login: account %d, ban: %dy %dm %dd %dh %dmn %ds\n", // char_data[i].account_id, (short)RFIFOW(fd,32), (short)RFIFOW(fd,34), (short)RFIFOW(fd,36), (short)RFIFOW(fd,38), (short)RFIFOW(fd,40), (short)RFIFOW(fd,42)); @@ -2001,7 +1944,7 @@ void parse_frommap(int fd) else { // character name not found - WFIFO_STRING(fd, 6, character_name, 24); + WFIFO_STRING(fd, 6, character_name.to__actual(), 24); WFIFOW(fd, 32) = 1; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline } // send answer if a player ask, not if the server ask @@ -2027,7 +1970,7 @@ void parse_frommap(int fd) p < RFIFOW(fd, 2) && j < ACCOUNT_REG2_NUM; p += 36, j++) { - RFIFO_STRING(fd, p, reg[j].str, 32); + reg[j].str = stringish(RFIFO_STRING<32>(fd, p)); reg[j].value = RFIFOL(fd, p + 32); } set_account_reg2(acc, j, reg); @@ -2079,29 +2022,14 @@ void parse_frommap(int fd) } static -int search_mapserver(const char *map) +int search_mapserver(XString map) { - int i, j; - char temp_map[16]; - int temp_map_len; - -// PRINTF("Searching the map-server for map '%s'... ", map); - strzcpy(temp_map, map, sizeof(temp_map)); - if (strchr(temp_map, '.') != NULL) - temp_map[strchr(temp_map, '.') - temp_map + 1] = '\0'; // suppress the '.gat', but conserve the '.' to be sure of the name of the map - - temp_map_len = strlen(temp_map); - for (i = 0; i < MAX_MAP_SERVERS; i++) + for (int i = 0; i < MAX_MAP_SERVERS; i++) if (server_fd[i] >= 0) - for (j = 0; server[i].maps[j][0]; j++) - //PRINTF("%s : %s = %d\n", server[i].map[j], map, strncmp(server[i].map[j], temp_map, temp_map_len)); - if (strncmp(server[i].maps[j], temp_map, temp_map_len) == 0) - { -// PRINTF("found -> server #%d.\n", i); + for (int j = 0; server[i].maps[j][0]; j++) + if (server[i].maps[j] == map) return i; - } -// PRINTF("not found.\n"); return -1; } @@ -2134,7 +2062,7 @@ int lan_ip_check(unsigned char *p) static void handle_x0066(int fd, struct char_session_data *sd, uint8_t rfifob_2, uint8_t *p) { - const char *ip = ip2str(session[fd]->client_addr.sin_addr); + IP_String ip = ip2str(session[fd]->client_addr.sin_addr); { mmo_charstatus *cd = nullptr; @@ -2164,7 +2092,7 @@ void handle_x0066(int fd, struct char_session_data *sd, uint8_t rfifob_2, uint8_ && server[j].maps[0][0]) { // change save point to one of map found on the server (the first) i = j; - strzcpy(cd->last_point.map_, server[j].maps[0], 16); + cd->last_point.map_ = server[j].maps[0]; PRINTF("Map-server #%d found with a map: '%s'.\n", j, server[j].maps[0]); // coordonates are unknown @@ -2187,7 +2115,7 @@ void handle_x0066(int fd, struct char_session_data *sd, uint8_t rfifob_2, uint8_ sd->account_id, cd->char_num, ip); PRINTF("--Send IP of map-server. "); if (lan_ip_check(p)) - WFIFOL(fd, 22) = inet_addr(lan_map_ip); + WFIFOL(fd, 22) = inet_addr(lan_map_ip.c_str()); else WFIFOL(fd, 22) = server[i].ip; WFIFOW(fd, 26) = server[i].port; @@ -2211,7 +2139,6 @@ void handle_x0066(int fd, struct char_session_data *sd, uint8_t rfifob_2, uint8_ static void parse_char(int fd) { - char email[40]; uint8_t *p = reinterpret_cast(&session[fd]->client_addr.sin_addr); if (login_fd < 0 || session[fd]->eof) @@ -2243,11 +2170,9 @@ void parse_char(int fd) { WFIFOW(login_fd, 0) = 0x2740; WFIFOL(login_fd, 2) = sd->account_id; - char old_pass[24]; - RFIFO_STRING(fd, 2, old_pass, 24); + AccountPass old_pass = stringish(RFIFO_STRING<24>(fd, 2)); WFIFO_STRING(login_fd, 6, old_pass, 24); - char new_pass[24]; - RFIFO_STRING(fd, 26, new_pass, 24); + AccountPass new_pass = stringish(RFIFO_STRING<24>(fd, 26)); WFIFO_STRING(login_fd, 30, new_pass, 24); WFIFOSET(login_fd, 54); } @@ -2269,7 +2194,7 @@ void parse_char(int fd) { session[fd]->session_data = make_unique(); sd = static_cast(session[fd]->session_data.get()); - strzcpy(sd->email, "no mail", 40); // put here a mail without '@' to refuse deletion if we don't receive the e-mail + sd->email = stringish("no mail"); // put here a mail without '@' to refuse deletion if we don't receive the e-mail sd->connect_until_time = TimeT(); // unknow or illimited (not displaying on map-server) } sd->account_id = RFIFOL(fd, 2); @@ -2354,8 +2279,7 @@ void parse_char(int fd) if (!sd || RFIFOREST(fd) < 37) return; { - char name[24]; - RFIFO_STRING(fd, 2, name, 24); + CharName name = stringish(RFIFO_STRING<24>(fd, 2)); uint8_t stats[6]; for (int i = 0; i < 6; ++i) stats[i] = RFIFOB(fd, 26 + i); @@ -2401,7 +2325,7 @@ void parse_char(int fd) WFIFOW(fd, 2 + 68) = cd->head_mid; WFIFOW(fd, 2 + 70) = cd->hair_color; - WFIFO_STRING(fd, 2 + 74, cd->name, 24); + WFIFO_STRING(fd, 2 + 74, cd->name.to__actual(), 24); WFIFOB(fd, 2 + 98) = min(cd->attrs[ATTR::STR], 255); WFIFOB(fd, 2 + 99) = min(cd->attrs[ATTR::AGI], 255); @@ -2419,9 +2343,10 @@ void parse_char(int fd) case 0x68: // delete char //Yor's Fix if (!sd || RFIFOREST(fd) < 46) return; - RFIFO_STRING(fd, 6, email, 40); - if (e_mail_check(email) == 0) - strzcpy(email, "a@a.com", 40); // default e-mail + { + AccountEmail email = stringish(RFIFO_STRING<40>(fd, 6)); + if (!e_mail_check(email)) + email = DEFAULT_EMAIL; { { @@ -2457,6 +2382,7 @@ void parse_char(int fd) WFIFOSET(fd, 3); } } + } x68_out: RFIFOSKIP(fd, 46); break; @@ -2472,12 +2398,10 @@ void parse_char(int fd) if (server_fd[i] < 0) break; } - char userid_[24]; - RFIFO_STRING(fd, 2, userid_, 24); - char passwd_[24]; - RFIFO_STRING(fd, 26, passwd_, 24); - if (i == MAX_MAP_SERVERS || strcmp(userid_, userid) - || strcmp(passwd_, passwd)) + AccountName userid_ = stringish(RFIFO_STRING<24>(fd, 2)); + AccountPass passwd_ = stringish(RFIFO_STRING<24>(fd, 26)); + if (i == MAX_MAP_SERVERS || userid_ != userid + || passwd_ != passwd) { WFIFOB(fd, 2) = 3; WFIFOSET(fd, 3); @@ -2495,8 +2419,8 @@ void parse_char(int fd) server[i].ip = RFIFOL(fd, 54); server[i].port = RFIFOW(fd, 58); server[i].users = 0; - for (char (&mapi)[16] : server[i].maps) - strzcpy(mapi, "", 16); + for (MapName& mapi : server[i].maps) + mapi = MapName(); WFIFOSET(fd, 3); RFIFOSKIP(fd, 60); realloc_fifo(fd, FIFOSIZE_SERVERLINK, @@ -2653,12 +2577,12 @@ void check_connect_login_server(TimerData *, tick_t) // Reading Lan Support configuration by [Yor] //------------------------------------------- static -int lan_config_read(const char *lancfgName) +int lan_config_read(ZString lancfgName) { struct hostent *h = NULL; // set default configuration - strzcpy(lan_map_ip, "127.0.0.1", sizeof(lan_map_ip)); + lan_map_ip = stringish("127.0.0.1"); subneti[0] = 127; subneti[1] = 0; subneti[2] = 0; @@ -2666,7 +2590,7 @@ int lan_config_read(const char *lancfgName) for (int j = 0; j < 4; j++) subnetmaski[j] = 255; - std::ifstream in(lancfgName); + std::ifstream in(lancfgName.c_str()); if (!in.is_open()) { @@ -2676,10 +2600,11 @@ int lan_config_read(const char *lancfgName) PRINTF("---start reading of Lan Support configuration...\n"); - std::string line; - while (std::getline(in, line)) + FString line; + while (io::getline(in, line)) { - std::string w1, w2; + SString w1; + TString w2; if (!split_key_value(line, &w1, &w2)) continue; @@ -2689,7 +2614,7 @@ int lan_config_read(const char *lancfgName) h = gethostbyname(w2.c_str()); if (h != NULL) { - sprintf(lan_map_ip, "%d.%d.%d.%d", + SNPRINTF(lan_map_ip, 16, "%d.%d.%d.%d", static_cast(h->h_addr[0]), static_cast(h->h_addr[1]), static_cast(h->h_addr[2]), @@ -2697,7 +2622,7 @@ int lan_config_read(const char *lancfgName) } else { - strzcpy(lan_map_ip, w2.c_str(), sizeof(lan_map_ip)); + lan_map_ip = stringish(w2); } PRINTF("LAN IP of map-server: %s.\n", lan_map_ip); } @@ -2742,14 +2667,15 @@ int lan_config_read(const char *lancfgName) } else { - PRINTF("WARNING: unknown lan config key: %s\n", w1); + FString w1z = w1; + PRINTF("WARNING: unknown lan config key: %s\n", w1z); } } // sub-network check of the map-server { unsigned char p[4]; - sscanf(lan_map_ip, "%hhu.%hhu.%hhu.%hhu", &p[0], &p[1], &p[2], &p[3]); + SSCANF(lan_map_ip, "%hhu.%hhu.%hhu.%hhu", &p[0], &p[1], &p[2], &p[3]); PRINTF("LAN test of LAN IP of the map-server: "); if (lan_ip_check(p) == 0) { @@ -2763,11 +2689,11 @@ int lan_config_read(const char *lancfgName) } static -int char_config_read(const char *cfgName) +int char_config_read(ZString cfgName) { struct hostent *h = NULL; - std::ifstream in(cfgName); + std::ifstream in(cfgName.c_str()); if (!in.is_open()) { @@ -2775,45 +2701,43 @@ int char_config_read(const char *cfgName) exit(1); } - std::string line; - while (std::getline(in, line)) + FString line; + while (io::getline(in, line)) { - std::string w1, w2; + SString w1; + TString w2; if (!split_key_value(line, &w1, &w2)) continue; if (w1 == "userid") - strzcpy(userid, w2.c_str(), 24); + userid = stringish(w2); else if (w1 == "passwd") - strzcpy(passwd, w2.c_str(), 24); + passwd = stringish(w2); else if (w1 == "server_name") { - strzcpy(server_name, w2.c_str(), sizeof(server_name)); + server_name = stringish(w2); PRINTF("%s server has been intialized\n", w2); } else if (w1 == "wisp_server_name") { if (w2.size() >= 4) - strzcpy(wisp_server_name, w2.c_str(), sizeof(wisp_server_name)); + wisp_server_name = stringish(w2); } else if (w1 == "login_ip") { h = gethostbyname(w2.c_str()); if (h != NULL) { - PRINTF("Login server IP address : %s -> %d.%d.%d.%d\n", w2, + SNPRINTF(login_ip_str, 16, "%d.%d.%d.%d", static_cast(h->h_addr[0]), static_cast(h->h_addr[1]), static_cast(h->h_addr[2]), static_cast(h->h_addr[3])); - sprintf(login_ip_str, "%d.%d.%d.%d", - static_cast(h->h_addr[0]), - static_cast(h->h_addr[1]), - static_cast(h->h_addr[2]), - static_cast(h->h_addr[3])); + PRINTF("Login server IP address : %s -> %s\n", + w2, login_ip_str); } else - strzcpy(login_ip_str, w2.c_str(), 16); + login_ip_str = stringish(w2); } else if (w1 == "login_port") { @@ -2824,20 +2748,16 @@ int char_config_read(const char *cfgName) h = gethostbyname(w2.c_str()); if (h != NULL) { - PRINTF("Character server IP address : %s -> %d.%d.%d.%d\n", - w2, + SNPRINTF(char_ip_str, 16, "%d.%d.%d.%d", static_cast(h->h_addr[0]), static_cast(h->h_addr[1]), static_cast(h->h_addr[2]), static_cast(h->h_addr[3])); - sprintf(char_ip_str, "%d.%d.%d.%d", - static_cast(h->h_addr[0]), - static_cast(h->h_addr[1]), - static_cast(h->h_addr[2]), - static_cast(h->h_addr[3])); + PRINTF("Character server IP address : %s -> %s\n", + w2, char_ip_str); } else - strzcpy(char_ip_str, w2.c_str(), 16); + char_ip_str = stringish(w2); } else if (w1 == "char_port") { @@ -2853,7 +2773,7 @@ int char_config_read(const char *cfgName) } else if (w1 == "char_txt") { - strzcpy(char_txt, w2.c_str(), sizeof(char_txt)); + char_txt = w2; } else if (w1 == "max_connect_user") { @@ -2863,7 +2783,7 @@ int char_config_read(const char *cfgName) } else if (w1 == "check_ip_flag") { - check_ip_flag = config_switch(w2.c_str()); + check_ip_flag = config_switch(w2); } else if (w1 == "autosave_time") { @@ -2873,16 +2793,7 @@ int char_config_read(const char *cfgName) } else if (w1 == "start_point") { - char map[32]; - int x, y; - if (SSCANF(w2, "%[^,],%d,%d", map, &x, &y) < 3) - continue; - if (strstr(map, ".gat") != NULL) - { // Verify at least if '.gat' is in the map name - strzcpy(start_point.map_, map, 16); - start_point.x = x; - start_point.y = y; - } + extract(w2, &start_point); } else if (w1 == "start_zeny") { @@ -2904,15 +2815,11 @@ int char_config_read(const char *cfgName) } else if (w1 == "unknown_char_name") { - strzcpy(unknown_char_name, w2.c_str(), 24); + unknown_char_name = stringish(w2); } else if (w1 == "char_log_filename") { - strzcpy(char_log_filename, w2.c_str(), sizeof(char_log_filename)); - } - else if (w1 == "name_ignoring_case") - { - name_ignoring_case = config_switch(w2.c_str()); + char_log_filename = w2; } else if (w1 == "char_name_option") { @@ -2920,15 +2827,16 @@ int char_config_read(const char *cfgName) } else if (w1 == "char_name_letters") { - strzcpy(char_name_letters, w2.c_str(), sizeof(char_name_letters)); + for (uint8_t c : w2) + char_name_letters[c] = true; } else if (w1 == "online_txt_filename") { - strzcpy(online_txt_filename, w2.c_str(), sizeof(online_txt_filename)); + online_txt_filename = w2; } else if (w1 == "online_html_filename") { - strzcpy(online_html_filename, w2.c_str(), sizeof(online_html_filename)); + online_html_filename = w2; } else if (w1 == "online_sorting_option") { @@ -2948,7 +2856,7 @@ int char_config_read(const char *cfgName) } else if (w1 == "anti_freeze_enable") { - anti_freeze_enable = config_switch(w2.c_str()); + anti_freeze_enable = config_switch(w2); } else if (w1 == "anti_freeze_interval") { @@ -2958,11 +2866,12 @@ int char_config_read(const char *cfgName) } else if (w1 == "import") { - char_config_read(w2.c_str()); + char_config_read(w2); } else { - PRINTF("WARNING: unknown char config key: %s\n", w1); + FString w1z = w1; + PRINTF("WARNING: unknown char config key: %s\n", w1z); } } @@ -2988,7 +2897,7 @@ void term_func(void) CHAR_LOG("----End of char-server (normal end with closing of all files).\n"); } -int do_init(int argc, char **argv) +int do_init(int argc, ZString *argv) { int i; @@ -2996,11 +2905,17 @@ int do_init(int argc, char **argv) CHAR_LOG(""); CHAR_LOG("The char-server starting...\n"); - char_config_read((argc < 2) ? CHAR_CONF_NAME : argv[1]); - lan_config_read((argc > 1) ? argv[1] : LOGIN_LAN_CONF_NAME); + if (argc > 1) + char_config_read(argv[1]); + else + char_config_read(CHAR_CONF_NAME); + if (argc > 1) + lan_config_read(argv[2]); + else + lan_config_read(LOGIN_LAN_CONF_NAME); - login_ip = inet_addr(login_ip_str); - char_ip = inet_addr(char_ip_str); + login_ip = inet_addr(login_ip_str.c_str()); + char_ip = inet_addr(char_ip_str.c_str()); for (i = 0; i < MAX_MAP_SERVERS; i++) { @@ -3013,7 +2928,10 @@ int do_init(int argc, char **argv) update_online = TimeT::now(); create_online_files(); // update online players files at start of the server - inter_init((argc > 2) ? argv[2] : inter_cfgName); // inter server 初期化 + if (argc > 3) + inter_init(argv[3]); + else + inter_init(inter_cfgName); // set_termfunc (do_final); set_defaultparse(parse_char); diff --git a/src/char/char.hpp b/src/char/char.hpp index 5e16a6a..8e37c64 100644 --- a/src/char/char.hpp +++ b/src/char/char.hpp @@ -15,18 +15,18 @@ struct mmo_map_server long ip; short port; int users; - char maps[MAX_MAP_PER_SERVER][16]; + MapName maps[MAX_MAP_PER_SERVER]; }; -const mmo_charstatus *search_character(const char *character_name); +const mmo_charstatus *search_character(CharName character_name); int mapif_sendall(const uint8_t *buf, unsigned int len); int mapif_sendallwos(int fd, const uint8_t *buf, unsigned int len); int mapif_send(int fd, const uint8_t *buf, unsigned int len); -void char_log(const_string line); +void char_log(XString line); #define CHAR_LOG(fmt, ...) \ - char_log(static_cast(STRPRINTF(fmt, ## __VA_ARGS__))) + char_log(STRPRINTF(fmt, ## __VA_ARGS__)) #endif // CHAR_HPP diff --git a/src/char/int_party.cpp b/src/char/int_party.cpp index 38edb9b..d2c64e1 100644 --- a/src/char/int_party.cpp +++ b/src/char/int_party.cpp @@ -3,8 +3,12 @@ #include #include +#include + #include "../common/cxxstdio.hpp" #include "../common/db.hpp" +#include "../common/extract.hpp" +#include "../common/io.hpp" #include "../common/lock.hpp" #include "../common/mmo.hpp" #include "../common/socket.hpp" @@ -14,7 +18,7 @@ #include "../poison.hpp" -char party_txt[1024] = "save/party.txt"; +FString party_txt = "save/party.txt"; static Map party_db; @@ -30,82 +34,84 @@ void mapif_parse_PartyLeave(int fd, int party_id, int account_id); // パーティデータの文字列への変換 static -std::string inter_party_tostr(struct party *p) +FString inter_party_tostr(struct party *p) { - std::string str = STRPRINTF( - "%d\t" - "%s\t" - "%d,%d\t", - p->party_id, - p->name, - p->exp, p->item); + MString str; + str += STRPRINTF( + "%d\t" + "%s\t" + "%d,%d\t", + p->party_id, + p->name, + p->exp, p->item); for (int i = 0; i < MAX_PARTY; i++) { struct party_member *m = &p->member[i]; + if (!m->account_id) + continue; str += STRPRINTF( "%d,%d\t" "%s\t", m->account_id, m->leader, - (m->account_id > 0) ? m->name : "NoMember"); + m->name); } - return str; + return FString(str); } -// パーティデータの文字列からの変換 static -int inter_party_fromstr(char *str, struct party *p) +bool extract(XString str, party *p) { *p = party(); - if (sscanf(str, - "%d\t" - "%[^\t]\t" - "%d,%d\t", - &p->party_id, - p->name, - &p->exp, &p->item) != 4) - return 1; - - for (int j = 0; j < 3 && str != NULL; j++) - str = strchr(str + 1, '\t'); - - for (int i = 0; i < MAX_PARTY; i++) + // not compatible with the normal extract()ors since it has + // a variable-size element that uses the same separator + std::vector bits; + if (!extract(str, vrec<'\t'>(&bits))) + return false; + auto begin = bits.begin(); + auto end = bits.end(); + if (begin == end || !extract(*begin, &p->party_id)) + return false; + ++begin; + if (begin == end || !extract(*begin, &p->name)) + return false; + ++begin; + if (begin == end || !extract(*begin, record<','>(&p->exp, &p->item))) + return false; + ++begin; + + for (int i = 0; begin != end && i < MAX_PARTY; ++i) { struct party_member *m = &p->member[i]; - if (str == NULL) - return 1; - - if (sscanf(str + 1, - "%d,%d\t" - "%[^\t]\t", - &m->account_id, &m->leader, - m->name) != 3) - return 1; - - for (int j = 0; j < 2 && str != NULL; j++) - str = strchr(str + 1, '\t'); + + if (begin == end || !extract(*begin, record<','>(&m->account_id, &m->leader))) + return false; + ++begin; + if (begin == end || !extract(*begin, &m->name)) + return false; + ++begin; + if (!m->account_id) + --i; } - return 0; + return true; } // パーティデータのロード int inter_party_init(void) { - char line[8192]; - FILE *fp; - int c = 0; - int i, j; - - if ((fp = fopen_(party_txt, "r")) == NULL) + std::ifstream in(party_txt.c_str()); + if (!in.is_open()) return 1; // TODO: convert to use char_id, and change to extract() - while (fgets(line, sizeof(line) - 1, fp)) + FString line; + int c = 0; + while (io::getline(in, line)) { - j = 0; - if (sscanf(line, "%d\t%%newid%%\n%n", &i, &j) == 1 && j > 0 + int i, j = 0; + if (SSCANF(line, "%d\t%%newid%%\n%n", &i, &j) == 1 && j > 0 && party_newid <= i) { party_newid = i; @@ -113,7 +119,7 @@ int inter_party_init(void) } struct party p {}; - if (inter_party_fromstr(line, &p) == 0 && p.party_id > 0) + if (extract(line, &p) && p.party_id > 0) { if (p.party_id >= party_newid) party_newid = p.party_id + 1; @@ -127,7 +133,6 @@ int inter_party_init(void) } c++; } - fclose_(fp); // PRINTF("int_party: %s read done (%d parties)\n", party_txt, c); return 0; @@ -137,7 +142,7 @@ int inter_party_init(void) static void inter_party_save_sub(struct party *data, FILE *fp) { - std::string line = inter_party_tostr(data); + FString line = inter_party_tostr(data); FPRINTF(fp, "%s\n", line); } @@ -164,15 +169,15 @@ int inter_party_save(void) // パーティ名検索用 static -void search_partyname_sub(struct party *p, const char *str, struct party **dst) +void search_partyname_sub(struct party *p, PartyName str, struct party **dst) { - if (strcasecmp(p->name, str) == 0) + if (p->name == str) *dst = p; } // パーティ名検索 static -struct party *search_partyname(const char *str) +struct party *search_partyname(PartyName str) { struct party *p = NULL; for (auto& pair : party_db) @@ -227,7 +232,7 @@ int party_check_empty(struct party *p) // キャラの競合がないかチェック用 static void party_check_conflict_sub(struct party *p, - int party_id, int account_id, const char *nick) + int party_id, int account_id, CharName nick) { int i; @@ -237,7 +242,7 @@ void party_check_conflict_sub(struct party *p, for (i = 0; i < MAX_PARTY; i++) { if (p->member[i].account_id == account_id - && strcmp(p->member[i].name, nick) == 0) + && p->member[i].name == nick) { // 別のパーティに偽の所属データがあるので脱退 PRINTF("int_party: party conflict! %d %d %d\n", account_id, @@ -249,7 +254,7 @@ void party_check_conflict_sub(struct party *p, // キャラの競合がないかチェック static -void party_check_conflict(int party_id, int account_id, const char *nick) +void party_check_conflict(int party_id, int account_id, CharName nick) { for (auto& pair : party_db) party_check_conflict_sub(&pair.second, @@ -342,14 +347,14 @@ void mapif_party_optionchanged(int fd, struct party *p, int account_id, // パーティ脱退通知 static -void mapif_party_leaved(int party_id, int account_id, char *name) +void mapif_party_leaved(int party_id, int account_id, CharName name) { unsigned char buf[34]; WBUFW(buf, 0) = 0x3824; WBUFL(buf, 2) = party_id; WBUFL(buf, 6) = account_id; - WBUF_STRING(buf, 10, name, 24); + WBUF_STRING(buf, 10, name.to__actual(), 24); mapif_sendall(buf, 34); PRINTF("int_party: party leaved %d %d %s\n", party_id, account_id, name); } @@ -382,9 +387,9 @@ void mapif_party_broken(int party_id, int flag) // パーティ内発言 static -void mapif_party_message(int party_id, int account_id, const char *mes) +void mapif_party_message(int party_id, int account_id, XString mes) { - size_t len = strlen(mes); + size_t len = mes.size() + 1; unsigned char buf[len + 12]; WBUFW(buf, 0) = 0x3827; @@ -400,14 +405,11 @@ void mapif_party_message(int party_id, int account_id, const char *mes) // パーティ static -void mapif_parse_CreateParty(int fd, int account_id, const char *name, const char *nick, - const char *map, int lv) +void mapif_parse_CreateParty(int fd, int account_id, PartyName name, CharName nick, + MapName map, int lv) { - int i; - - for (i = 0; i < 24 && name[i]; i++) { - if (!(name[i] & 0xe0) || name[i] == 0x7f) + if (!name.is_print()) { PRINTF("int_party: illegal party name [%s]\n", name); mapif_party_created(fd, account_id, NULL); @@ -423,12 +425,12 @@ void mapif_parse_CreateParty(int fd, int account_id, const char *name, const cha } struct party p {}; p.party_id = party_newid++; - strzcpy(p.name, name, 24); + p.name = name; p.exp = 0; p.item = 0; p.member[0].account_id = account_id; - strzcpy(p.member[0].name, nick, 24); - strzcpy(p.member[0].map, map, 16); + p.member[0].name = nick; + p.member[0].map = map; p.member[0].leader = 1; p.member[0].online = 1; p.member[0].lv = lv; @@ -453,7 +455,7 @@ void mapif_parse_PartyInfo(int fd, int party_id) // パーティ追加要求 static void mapif_parse_PartyAddMember(int fd, int party_id, int account_id, - const char *nick, const char *map, int lv) + CharName nick, MapName map, int lv) { struct party *p = party_db.search(party_id); if (p == NULL) @@ -469,8 +471,8 @@ void mapif_parse_PartyAddMember(int fd, int party_id, int account_id, int flag = 0; p->member[i].account_id = account_id; - strzcpy(p->member[i].name, nick, 24); - strzcpy(p->member[i].map, map, 16); + p->member[i].name = nick; + p->member[i].map = map; p->member[i].leader = 0; p->member[i].online = 1; p->member[i].lv = lv; @@ -493,7 +495,7 @@ void mapif_parse_PartyAddMember(int fd, int party_id, int account_id, // パーティー設定変更要求 static void mapif_parse_PartyChangeOption(int fd, int party_id, int account_id, - int exp, int item) + int exp, int item) { struct party *p = party_db.search(party_id); if (p == NULL) @@ -534,7 +536,7 @@ void mapif_parse_PartyLeave(int, int party_id, int account_id) // パーティマップ更新要求 static void mapif_parse_PartyChangeMap(int fd, int party_id, int account_id, - const char *map, int online, int lv) + MapName map, int online, int lv) { struct party *p = party_db.search(party_id); if (p == NULL) @@ -546,7 +548,7 @@ void mapif_parse_PartyChangeMap(int fd, int party_id, int account_id, continue; int flag = 0; - strzcpy(p->member[i].map, map, 16); + p->member[i].map = map; p->member[i].online = online; p->member[i].lv = lv; mapif_party_membermoved(p, i); @@ -576,14 +578,14 @@ void mapif_parse_BreakParty(int fd, int party_id) // パーティメッセージ送信 static -void mapif_parse_PartyMessage(int, int party_id, int account_id, const char *mes) +void mapif_parse_PartyMessage(int, int party_id, int account_id, XString mes) { mapif_party_message(party_id, account_id, mes); } // パーティチェック要求 static -void mapif_parse_PartyCheck(int, int party_id, int account_id, const char *nick) +void mapif_parse_PartyCheck(int, int party_id, int account_id, CharName nick) { party_check_conflict(party_id, account_id, nick); } @@ -600,12 +602,9 @@ int inter_party_parse_frommap(int fd) case 0x3020: { int account = RFIFOL(fd, 2); - char name[24]; - RFIFO_STRING(fd, 6, name, 24); - char nick[24]; - RFIFO_STRING(fd, 30, nick, 24); - char map[16]; - RFIFO_STRING(fd, 54, map, 16); + PartyName name = stringish(RFIFO_STRING<24>(fd, 6)); + CharName nick = stringish(RFIFO_STRING<24>(fd, 30)); + MapName map = RFIFO_STRING<16>(fd, 54); uint16_t lv = RFIFOW(fd, 70); mapif_parse_CreateParty(fd, account, @@ -625,10 +624,8 @@ int inter_party_parse_frommap(int fd) { int party_id = RFIFOL(fd, 2); int account_id = RFIFOL(fd, 6); - char nick[24]; - RFIFO_STRING(fd, 10, nick, 24); - char map[16]; - RFIFO_STRING(fd, 34, map, 16); + CharName nick = stringish(RFIFO_STRING<24>(fd, 10)); + MapName map = RFIFO_STRING<16>(fd, 34); uint16_t lv = RFIFOW(fd, 50); mapif_parse_PartyAddMember(fd, party_id, @@ -664,8 +661,7 @@ int inter_party_parse_frommap(int fd) { int party_id = RFIFOL(fd, 2); int account_id = RFIFOL(fd, 6); - char map[16]; - RFIFO_STRING(fd, 10, map, 16); + MapName map = RFIFO_STRING<16>(fd, 10); uint8_t online = RFIFOB(fd, 26); uint16_t lv = RFIFOW(fd, 27); mapif_parse_PartyChangeMap(fd, @@ -687,8 +683,7 @@ int inter_party_parse_frommap(int fd) size_t len = RFIFOW(fd, 2) - 12; int party_id = RFIFOL(fd, 4); int account_id = RFIFOL(fd, 8); - char mes[len]; - RFIFO_STRING(fd, 12, mes, len); + FString mes = RFIFO_STRING(fd, 12, len); mapif_parse_PartyMessage(fd, party_id, account_id, @@ -699,8 +694,7 @@ int inter_party_parse_frommap(int fd) { int party_id = RFIFOL(fd, 2); int account_id = RFIFOL(fd, 6); - char nick[24]; - RFIFO_STRING(fd, 10, nick, 24); + CharName nick = stringish(RFIFO_STRING<24>(fd, 10)); mapif_parse_PartyCheck(fd, party_id, account_id, diff --git a/src/char/int_party.hpp b/src/char/int_party.hpp index 8a59b49..d003250 100644 --- a/src/char/int_party.hpp +++ b/src/char/int_party.hpp @@ -1,6 +1,8 @@ #ifndef INT_PARTY_HPP #define INT_PARTY_HPP +#include "../common/strings.hpp" + int inter_party_init(void); int inter_party_save(void); @@ -8,6 +10,6 @@ int inter_party_parse_frommap(int fd); void inter_party_leave(int party_id, int account_id); -extern char party_txt[1024]; +extern FString party_txt; #endif // INT_PARTY_HPP diff --git a/src/char/int_storage.cpp b/src/char/int_storage.cpp index 09ce9d4..aa605bf 100644 --- a/src/char/int_storage.cpp +++ b/src/char/int_storage.cpp @@ -9,6 +9,7 @@ #include "../common/cxxstdio.hpp" #include "../common/db.hpp" #include "../common/extract.hpp" +#include "../common/io.hpp" #include "../common/lock.hpp" #include "../common/mmo.hpp" #include "../common/socket.hpp" @@ -17,16 +18,17 @@ // ファイル名のデフォルト // inter_config_read()で再設定される -char storage_txt[1024] = "save/storage.txt"; +FString storage_txt = "save/storage.txt"; static Map storage_db; // 倉庫データを文字列に変換 static -std::string storage_tostr(struct storage *p) +FString storage_tostr(struct storage *p) { - std::string str = STRPRINTF( + MString str; + str += STRPRINTF( "%d,%d\t", p->account_id, p->storage_amount); @@ -53,13 +55,13 @@ std::string storage_tostr(struct storage *p) str += '\t'; if (!f) - str.clear(); - return str; + return FString(); + return FString(str); } // 文字列を倉庫データに変換 static -bool extract(const_string str, struct storage *p) +bool extract(XString str, struct storage *p) { std::vector storage_items; if (!extract(str, @@ -73,7 +75,7 @@ bool extract(const_string str, struct storage *p) if (p->account_id <= 0) return false; - if (storage_items.size() >= MAX_STORAGE) + if (storage_items.size() > MAX_STORAGE) return false; std::copy(storage_items.begin(), storage_items.end(), p->storage_); @@ -98,15 +100,15 @@ int inter_storage_init(void) { int c = 0; - std::ifstream in(storage_txt); + std::ifstream in(storage_txt.c_str()); if (!in.is_open()) { PRINTF("cant't read : %s\n", storage_txt); return 1; } - std::string line; - while (std::getline(in, line)) + FString line; + while (io::getline(in, line)) { struct storage s {}; if (extract(line, &s)) @@ -127,8 +129,8 @@ int inter_storage_init(void) static void inter_storage_save_sub(struct storage *data, FILE *fp) { - std::string line = storage_tostr(data); - if (!line.empty()) + FString line = storage_tostr(data); + if (line) FPRINTF(fp, "%s\n", line); } diff --git a/src/char/int_storage.hpp b/src/char/int_storage.hpp index 691f16d..a03d70f 100644 --- a/src/char/int_storage.hpp +++ b/src/char/int_storage.hpp @@ -1,6 +1,8 @@ #ifndef INT_STORAGE_HPP #define INT_STORAGE_HPP +#include "../common/strings.hpp" + int inter_storage_init(void); int inter_storage_save(void); void inter_storage_delete(int account_id); @@ -8,6 +10,6 @@ struct storage *account2storage(int account_id); int inter_storage_parse_frommap(int fd); -extern char storage_txt[1024]; +extern FString storage_txt; #endif // INT_STORAGE_HPP diff --git a/src/char/inter.cpp b/src/char/inter.cpp index 68d81e8..3cb51e7 100644 --- a/src/char/inter.cpp +++ b/src/char/inter.cpp @@ -10,6 +10,7 @@ #include "../common/cxxstdio.hpp" #include "../common/db.hpp" #include "../common/extract.hpp" +#include "../common/io.hpp" #include "../common/lock.hpp" #include "../common/socket.hpp" #include "../common/timer.hpp" @@ -25,10 +26,10 @@ // that is the waiting time of answers of all map-servers constexpr std::chrono::minutes WISDATA_TTL = std::chrono::minutes(1); -char inter_log_filename[1024] = "log/inter.log"; +FString inter_log_filename = "log/inter.log"; static -char accreg_txt[1024] = "save/accreg.txt"; +FString accreg_txt = "save/accreg.txt"; struct accreg { @@ -59,8 +60,8 @@ struct WisData { int id, fd, count; tick_t tick; - char src[24], dst[24]; - std::string msg; + CharName src, dst; + FString msg; }; static Map wis_db; @@ -71,17 +72,18 @@ std::vector wis_dellistv; // アカウント変数を文字列へ変換 static -std::string inter_accreg_tostr(struct accreg *reg) +FString inter_accreg_tostr(struct accreg *reg) { - std::string str STRPRINTF("%d\t", reg->account_id); + MString str; + str += STRPRINTF("%d\t", reg->account_id); for (int j = 0; j < reg->reg_num; j++) str += STRPRINTF("%s,%d ", reg->reg[j].str, reg->reg[j].value); - return str; + return FString(str); } // アカウント変数を文字列から変換 static -bool extract(const_string str, struct accreg *reg) +bool extract(XString str, struct accreg *reg) { std::vector vars; if (!extract(str, @@ -105,11 +107,11 @@ int inter_accreg_init(void) { int c = 0; - std::ifstream in(accreg_txt); + std::ifstream in(accreg_txt.c_str()); if (!in.is_open()) return 1; - std::string line; - while (std::getline(in, line)) + FString line; + while (io::getline(in, line)) { struct accreg reg {}; if (extract(line, ®)) @@ -133,7 +135,7 @@ void inter_accreg_save_sub(struct accreg *reg, FILE *fp) { if (reg->reg_num > 0) { - std::string line = inter_accreg_tostr(reg); + FString line = inter_accreg_tostr(reg); fwrite(line.data(), 1, line.size(), fp); fputc('\n', fp); } @@ -166,33 +168,34 @@ int inter_accreg_save(void) *------------------------------------------ */ static -int inter_config_read(const char *cfgName) +int inter_config_read(ZString cfgName) { - std::ifstream in(cfgName); + std::ifstream in(cfgName.c_str()); if (!in.is_open()) { PRINTF("file not found: %s\n", cfgName); return 1; } - std::string line; - while (std::getline(in, line)) + FString line; + while (io::getline(in, line)) { - std::string w1, w2; + SString w1; + TString w2; if (!split_key_value(line, &w1, &w2)) continue; if (w1 == "storage_txt") { - strzcpy(storage_txt, w2.c_str(), sizeof(storage_txt)); + storage_txt = w2; } else if (w1 == "party_txt") { - strzcpy(party_txt, w2.c_str(), sizeof(party_txt)); + party_txt = w2; } else if (w1 == "accreg_txt") { - strzcpy(accreg_txt, w2.c_str(), sizeof(accreg_txt)); + accreg_txt = w2; } else if (w1 == "party_share_level") { @@ -202,15 +205,16 @@ int inter_config_read(const char *cfgName) } else if (w1 == "inter_log_filename") { - strzcpy(inter_log_filename, w2.c_str(), sizeof(inter_log_filename)); + inter_log_filename = w2; } else if (w1 == "import") { - inter_config_read(w2.c_str()); + inter_config_read(w2); } else { - PRINTF("WARNING: unknown inter config key: %s\n", w1); + FString w1z = w1; + PRINTF("WARNING: unknown inter config key: %s\n", w1z); } } @@ -218,25 +222,21 @@ int inter_config_read(const char *cfgName) } // セーブ -int inter_save(void) +void inter_save(void) { inter_party_save(); inter_storage_save(); inter_accreg_save(); - - return 0; } // 初期化 -int inter_init(const char *file) +void inter_init(ZString file) { inter_config_read(file); inter_party_init(); inter_storage_init(); inter_accreg_init(); - - return 0; } //-------------------------------------------------------- @@ -244,9 +244,9 @@ int inter_init(const char *file) // GMメッセージ送信 static -void mapif_GMmessage(const char *mes) +void mapif_GMmessage(XString mes) { - size_t str_len = strlen(mes) + 1; + size_t str_len = mes.size() + 1; size_t msg_len = str_len + 4; uint8_t buf[msg_len]; @@ -266,9 +266,9 @@ void mapif_wis_message(struct WisData *wd) WBUFW(buf, 0) = 0x3801; WBUFW(buf, 2) = 56 + str_size; WBUFL(buf, 4) = wd->id; - WBUF_STRING(buf, 8, wd->src, 24); - WBUF_STRING(buf, 32, wd->dst, 24); - WBUF_STRING(buf, 56, wd->msg.c_str(), str_size); + WBUF_STRING(buf, 8, wd->src.to__actual(), 24); + WBUF_STRING(buf, 32, wd->dst.to__actual(), 24); + WBUF_STRING(buf, 56, wd->msg, str_size); wd->count = mapif_sendall(buf, WBUFW(buf, 2)); } @@ -279,7 +279,7 @@ void mapif_wis_end(struct WisData *wd, int flag) uint8_t buf[27]; WBUFW(buf, 0) = 0x3802; - WBUF_STRING(buf, 2, wd->src, 24); + WBUF_STRING(buf, 2, wd->src.to__actual(), 24); WBUFB(buf, 26) = flag; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target mapif_send(wd->fd, buf, 27); } @@ -362,8 +362,7 @@ void mapif_parse_GMmessage(int fd) { size_t msg_len = RFIFOW(fd, 2); size_t str_len = msg_len - 4; - char buf[str_len]; - RFIFO_STRING(fd, 4, buf, str_len); + FString buf = RFIFO_STRING(fd, 4, str_len); mapif_GMmessage(buf); } @@ -380,10 +379,8 @@ void mapif_parse_WisRequest(int fd) return; } - char from[24]; - char to[24]; - RFIFO_STRING(fd, 4, from, 24); - RFIFO_STRING(fd, 28, to, 24); + CharName from = stringish(RFIFO_STRING<24>(fd, 4)); + CharName to = stringish(RFIFO_STRING<24>(fd, 28)); // search if character exists before to ask all map-servers const mmo_charstatus *mcs = search_character(to); @@ -391,7 +388,7 @@ void mapif_parse_WisRequest(int fd) { uint8_t buf[27]; WBUFW(buf, 0) = 0x3802; - WBUF_STRING(buf, 2, from, 24); + WBUF_STRING(buf, 2, from.to__actual(), 24); WBUFB(buf, 26) = 1; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target mapif_send(fd, buf, 27); // Character exists. So, ask all map-servers @@ -399,19 +396,19 @@ void mapif_parse_WisRequest(int fd) else { // to be sure of the correct name, rewrite it - strzcpy(to, mcs->name, 24); + to = mcs->name; // if source is destination, don't ask other servers. - if (strcmp(from, to) == 0) + if (from == to) { uint8_t buf[27]; WBUFW(buf, 0) = 0x3802; - WBUF_STRING(buf, 2, from, 24); + WBUF_STRING(buf, 2, from.to__actual(), 24); WBUFB(buf, 26) = 1; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target mapif_send(fd, buf, 27); } else { - struct WisData wd; + struct WisData wd {}; // Whether the failure of previous wisp/page transmission (timeout) check_ttl_wisdata(); @@ -419,11 +416,9 @@ void mapif_parse_WisRequest(int fd) wd.id = ++wisid; wd.fd = fd; size_t len = RFIFOW(fd, 2) - 52; - RFIFO_STRING(fd, 4, wd.src, 24); - RFIFO_STRING(fd, 28, wd.dst, 24); - char tmpbuf[len]; - RFIFO_STRING(fd, 52, tmpbuf, len); - wd.msg = std::string(tmpbuf); + wd.src = from; + wd.dst = to; + wd.msg = RFIFO_STRING(fd, 52, len); wd.tick = gettick(); wis_db.insert(wd.id, wd); mapif_wis_message(&wd); @@ -480,7 +475,7 @@ void mapif_parse_AccReg(int fd) for (j = 0, p = 8; j < ACCOUNT_REG_NUM && p < RFIFOW(fd, 2); j++, p += 36) { - RFIFO_STRING(fd, p, reg->reg[j].str, 32); + reg->reg[j].str = stringish(RFIFO_STRING<32>(fd, p)); reg->reg[j].value = RFIFOL(fd, p + 32); } reg->reg_num = j; diff --git a/src/char/inter.hpp b/src/char/inter.hpp index 0adbf03..ce8447d 100644 --- a/src/char/inter.hpp +++ b/src/char/inter.hpp @@ -1,8 +1,10 @@ #ifndef INTER_HPP #define INTER_HPP -int inter_init(const char *file); -int inter_save(void); +#include "../common/strings.hpp" + +void inter_init(ZString file); +void inter_save(void); int inter_parse_frommap(int fd); int inter_check_length(int fd, int length); @@ -10,6 +12,6 @@ int inter_check_length(int fd, int length); #define inter_cfgName "conf/inter_athena.conf" extern int party_share_level; -extern char inter_log_filename[1024]; +extern FString inter_log_filename; #endif // INTER_HPP diff --git a/src/common/const_array.hpp b/src/common/const_array.hpp index a3a6d58..314eccf 100644 --- a/src/common/const_array.hpp +++ b/src/common/const_array.hpp @@ -25,11 +25,11 @@ #include #include -#include #include #ifdef WORKAROUND_GCC46_COMPILER // constexpr is buggy with templates in this version +// Is this still needed now that const_string is removed? # define constexpr /* nothing */ #endif @@ -125,57 +125,8 @@ public: } }; -// subclass just provides a simpler name and some conversions -// Important note: it must be safe to dereference end, though -// the value is unspecified. -class const_string : public const_array -{ -public: - // Implicit conversion from C string. - constexpr - const_string(const char *z) - : const_array(z, z ? strlen(z) : 0) - {} - - // Same as parent constructor. - constexpr - const_string(const char *s, size_t l) - : const_array(s, l) - {} - - // Same as parent constructor. - constexpr - const_string(const char *b, const char *e) - : const_array(b, e) - {} - - // Same as parent constructor. - const_string(const std::vector s) - : const_array(s) - {} - - // Implicit conversion from C++ string. - const_string(const std::string& s) - : const_array(s.data(), s.size()) - {} - - // but disallow converion from a temporary. - const_string(std::string&&) = delete; - - // allow being sloppy - constexpr - const_string(const_array a) - : const_array(a) - {} -}; #ifdef WORKAROUND_GCC46_COMPILER # undef constexpr #endif -inline -std::ostream& operator << (std::ostream& o, const_string s) -{ - return o.write(s.data(), s.size()); -} - #endif // CONST_ARRAY_HPP diff --git a/src/common/core.cpp b/src/common/core.cpp index 153414d..76aa09c 100644 --- a/src/common/core.cpp +++ b/src/common/core.cpp @@ -76,7 +76,11 @@ int main(int argc, char **argv) { do_socket(); - do_init(argc, argv); + // ZString args[argc]; is (deliberately!) not supported by clang yet + ZString *args = static_cast(alloca(argc * sizeof(ZString))); + for (int i = 0; i < argc; ++i) + args[i] = ZString(ZString::really_construct_from_a_pointer, argv[i], nullptr); + do_init(argc, args); // set up exit handlers *after* the initialization has happened. // This is because term_func is likely to depend on successful init. diff --git a/src/common/core.hpp b/src/common/core.hpp index 0c11efb..f1473ed 100644 --- a/src/common/core.hpp +++ b/src/common/core.hpp @@ -3,6 +3,8 @@ #include "sanity.hpp" +#include "strings.hpp" + /// core.c contains a server-independent main() function /// and then runs a do_sendrecv loop @@ -12,7 +14,7 @@ extern bool runflag; /// This is an external function defined by each server /// This function must register stuff for the parse loop -extern int do_init(int, char **); +extern int do_init(int, ZString *); /// Cleanup function called whenever a signal kills us /// or when if we manage to exit() gracefully. diff --git a/src/common/cxxstdio.cpp b/src/common/cxxstdio.cpp deleted file mode 100644 index 8f4001f..0000000 --- a/src/common/cxxstdio.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "cxxstdio.hpp" -// cxxstdio.cpp - pass C++ types through scanf/printf -// -// Copyright © 2013 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 - -namespace cxxstdio -{ -StringConverter::~StringConverter() -{ - if (mid) - { - out = mid; - free(mid); - } -} -} // namespace cxxstdio diff --git a/src/common/cxxstdio.hpp b/src/common/cxxstdio.hpp index 96c3ca2..89cc5de 100644 --- a/src/common/cxxstdio.hpp +++ b/src/common/cxxstdio.hpp @@ -24,37 +24,19 @@ #include #include -#include - #include "const_array.hpp" #include "utils2.hpp" namespace cxxstdio { + // other implementations of do_vprint or do_vscan are injected by ADL. inline __attribute__((format(printf, 2, 0))) int do_vprint(FILE *out, const char *fmt, va_list ap) { return vfprintf(out, fmt, ap); } - inline __attribute__((format(printf, 2, 0))) - int do_vprint(std::string& out, const char *fmt, va_list ap) - { - int len; - { - va_list ap2; - va_copy(ap2, ap); - len = vsnprintf(nullptr, 0, fmt, ap2); - va_end(ap2); - } - char buffer[len + 1]; - vsnprintf(buffer, len + 1, fmt, ap); - - out = buffer; - return len; - } - inline __attribute__((format(scanf, 2, 0))) int do_vscan(FILE *in, const char *fmt, va_list ap) { @@ -72,13 +54,6 @@ namespace cxxstdio int do_vscan(const char *, const char *, va_list) = delete; #endif - inline __attribute__((format(scanf, 2, 0))) - int do_vscan(const std::string& in, const char *fmt, va_list ap) - { - return vsscanf(in.c_str(), fmt, ap); - } - - template inline __attribute__((format(printf, 2, 3))) int do_print(T&& t, const char *fmt, ...) throw() @@ -104,13 +79,19 @@ namespace cxxstdio } - template - typename remove_enum::type convert_for_printf(T v) + template::value>::type> + typename remove_enum::type decay_for_printf(T v) { typedef typename remove_enum::type repr_type; return repr_type(v); } + template()))> + T&& convert_for_printf(T&& v) + { + return std::forward(v); + } + template::value>::type> T& convert_for_scanf(T& v) { @@ -152,7 +133,7 @@ namespace cxxstdio } template constexpr - E get_max_value(E) + E get_enum_max_value(E) { return E::max_value; } @@ -199,34 +180,6 @@ namespace cxxstdio return v; } - - inline - const char *convert_for_printf(const std::string& s) - { - return s.c_str(); - } - - class StringConverter - { - std::string& out; - char *mid; - public: - StringConverter(std::string& s) - : out(s), mid(nullptr) - {} - ~StringConverter(); - char **operator &() - { - return ∣ - } - }; - - inline - StringConverter convert_for_scanf(std::string& s) - { - return StringConverter(s); - } - template class PrintFormatter { @@ -238,7 +191,7 @@ namespace cxxstdio constexpr static const char *print_format = Format::print_format(); return do_print(std::forward(t), print_format, - convert_for_printf(std::forward(a))...); + decay_for_printf(convert_for_printf(std::forward(a)))...); } }; @@ -257,7 +210,7 @@ namespace cxxstdio } }; -#define FPRINTF(file, fmt, ...) \ +#define XPRINTF(out, fmt, ...) \ ([&]() -> int \ { \ struct format_impl \ @@ -265,10 +218,10 @@ namespace cxxstdio constexpr static \ const char *print_format() { return fmt; } \ }; \ - return cxxstdio::PrintFormatter::print(file, ## __VA_ARGS__); \ + return cxxstdio::PrintFormatter::print(out, ## __VA_ARGS__); \ }()) -#define FSCANF(file, fmt, ...) \ +#define XSCANF(out, fmt, ...) \ ([&]() -> int \ { \ struct format_impl \ @@ -276,22 +229,33 @@ namespace cxxstdio constexpr static \ const char *scan_format() { return fmt; } \ }; \ - return cxxstdio::ScanFormatter::scan(file, ## __VA_ARGS__); \ + return cxxstdio::ScanFormatter::scan(out, ## __VA_ARGS__); \ }()) +#define FPRINTF(file, fmt, ...) XPRINTF(no_cast(file), fmt, ## __VA_ARGS__) +#define FSCANF(file, fmt, ...) XSCANF(no_cast(file), fmt, ## __VA_ARGS__) #define PRINTF(fmt, ...) FPRINTF(stdout, fmt, ## __VA_ARGS__) -#define SPRINTF(str, fmt, ...) FPRINTF(str, fmt, ## __VA_ARGS__) +#define SPRINTF(str, fmt, ...) XPRINTF(base_cast(str), fmt, ## __VA_ARGS__) +#define SNPRINTF(str, n, fmt, ...) XPRINTF(base_cast&>(str), fmt, ## __VA_ARGS__) #define SCANF(fmt, ...) FSCANF(stdin, fmt, ## __VA_ARGS__) -#define SSCANF(str, fmt, ...) FSCANF(str, fmt, ## __VA_ARGS__) +#define SSCANF(str, fmt, ...) XSCANF(/*ZString or compatible*/str, fmt, ## __VA_ARGS__) #define STRPRINTF(fmt, ...) \ - ([&]() -> std::string \ + ([&]() -> FString \ { \ - std::string _out_impl; \ + FString _out_impl; \ SPRINTF(_out_impl, fmt, ## __VA_ARGS__);\ return _out_impl; \ }()) +#define STRNPRINTF(n, fmt, ...) \ + ([&]() -> VString \ + { \ + VString _out_impl; \ + SNPRINTF(_out_impl, n, fmt, ## __VA_ARGS__);\ + return _out_impl; \ + }()) + } // namespace cxxstdio #endif // CXXSTDIO_HPP diff --git a/src/common/cxxstdio_test.cpp b/src/common/cxxstdio_test.cpp new file mode 100644 index 0000000..9b6eeb2 --- /dev/null +++ b/src/common/cxxstdio_test.cpp @@ -0,0 +1,3 @@ +#include "cxxstdio.hpp" + +#include diff --git a/src/common/dumb_ptr.hpp b/src/common/dumb_ptr.hpp index 8863ef2..fe5031a 100644 --- a/src/common/dumb_ptr.hpp +++ b/src/common/dumb_ptr.hpp @@ -202,25 +202,25 @@ struct dumb_string { return dumb_string::copy(sz, sz + strlen(sz)); } - static dumb_string copys(const std::string& s) + static dumb_string copys(XString s) { return dumb_string::copy(&*s.begin(), &*s.end()); } - static dumb_string copyn(const char *sn, size_t n) + static +#ifndef __clang__ + __attribute__((warning("shouldn't use this - slice instead"))) +#endif + dumb_string copyn(const char *sn, size_t n) { return dumb_string::copy(sn, sn + strnlen(sn, n)); } - static dumb_string copyc(const_string s) - { - return dumb_string::copy(s.begin(), s.end()); - } static - dumb_string fake(const char *p) + dumb_string fake(ZString p) { dumb_string rv; - size_t len = p ? strlen(p) : 0; - rv.impl = dumb_ptr(const_cast(p), len); + size_t len = p.size(); + rv.impl = dumb_ptr(const_cast(p.c_str()), len); return rv; } @@ -235,17 +235,17 @@ struct dumb_string const char *c_str() const { - return impl ? &impl[0] : ""; + return &impl[0]; } - std::string str() const + operator ZString() const { - return c_str(); + return ZString(ZString::really_construct_from_a_pointer, c_str(), nullptr); } - operator const_string() const + FString str() const { - return const_string(c_str()); + return ZString(*this); } char& operator[](size_t i) const @@ -262,117 +262,8 @@ struct dumb_string { return !impl; } - - operator ZString() { return ZString(ZString::really_construct_from_a_pointer, c_str()); } - -#if 0 - friend bool operator == (dumb_string l, dumb_string r) - { - return l.impl == r.impl; - } - friend bool operator != (dumb_string l, dumb_string r) - { - return !(l == r); - } -#endif }; -namespace operators -{ - inline - bool operator == (dumb_string l, dumb_string r) - { - return strcmp(l.c_str(), r.c_str()) == 0; - } - inline - bool operator != (dumb_string l, dumb_string r) - { - return strcmp(l.c_str(), r.c_str()) != 0; - } - inline - bool operator < (dumb_string l, dumb_string r) - { - return strcmp(l.c_str(), r.c_str()) < 0; - } - inline - bool operator <= (dumb_string l, dumb_string r) - { - return strcmp(l.c_str(), r.c_str()) <= 0; - } - inline - bool operator > (dumb_string l, dumb_string r) - { - return strcmp(l.c_str(), r.c_str()) > 0; - } - inline - bool operator >= (dumb_string l, dumb_string r) - { - return strcmp(l.c_str(), r.c_str()) >= 0; - } - - inline - bool operator == (const char *l, dumb_string r) - { - return strcmp(l, r.c_str()) == 0; - } - inline - bool operator != (const char *l, dumb_string r) - { - return strcmp(l, r.c_str()) != 0; - } - inline - bool operator < (const char *l, dumb_string r) - { - return strcmp(l, r.c_str()) < 0; - } - inline - bool operator <= (const char *l, dumb_string r) - { - return strcmp(l, r.c_str()) <= 0; - } - inline - bool operator > (const char *l, dumb_string r) - { - return strcmp(l, r.c_str()) > 0; - } - inline - bool operator >= (const char *l, dumb_string r) - { - return strcmp(l, r.c_str()) >= 0; - } - - inline - bool operator == (dumb_string l, const char *r) - { - return strcmp(l.c_str(), r) == 0; - } - inline - bool operator != (dumb_string l, const char *r) - { - return strcmp(l.c_str(), r) != 0; - } - inline - bool operator < (dumb_string l, const char *r) - { - return strcmp(l.c_str(), r) < 0; - } - inline - bool operator <= (dumb_string l, const char *r) - { - return strcmp(l.c_str(), r) <= 0; - } - inline - bool operator > (dumb_string l, const char *r) - { - return strcmp(l.c_str(), r) > 0; - } - inline - bool operator >= (dumb_string l, const char *r) - { - return strcmp(l.c_str(), r) >= 0; - } -} - inline const char *convert_for_printf(dumb_string ds) { diff --git a/src/common/extract.cpp b/src/common/extract.cpp index 5e89e19..720e6df 100644 --- a/src/common/extract.cpp +++ b/src/common/extract.cpp @@ -18,28 +18,31 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -bool extract(const_string str, const_string *rv) +#include "../poison.hpp" + +bool extract(XString str, XString *rv) { *rv = str; return true; } -bool extract(const_string str, std::string *rv) +bool extract(XString str, FString *rv) { - *rv = std::string(str.begin(), str.end()); + *rv = str; return true; } -bool extract(const_string str, struct global_reg *var) +bool extract(XString str, struct global_reg *var) { return extract(str, record<','>(&var->str, &var->value)); } -bool extract(const_string str, struct item *it) +bool extract(XString str, struct item *it) { + it->broken = 0; return extract(str, - record<','>( + record<',', 11>( &it->id, &it->nameid, &it->amount, @@ -51,18 +54,5 @@ bool extract(const_string str, struct item *it) &it->card[1], &it->card[2], &it->card[3], - &it->broken)) - || extract(str, - record<','>( - &it->id, - &it->nameid, - &it->amount, - &it->equip, - &it->identify, - &it->refine, - &it->attribute, - &it->card[0], - &it->card[1], - &it->card[2], - &it->card[3])); + &it->broken)); } diff --git a/src/common/extract.hpp b/src/common/extract.hpp index ae1a74b..3c24693 100644 --- a/src/common/extract.hpp +++ b/src/common/extract.hpp @@ -27,14 +27,15 @@ #include "mmo.hpp" #include "utils.hpp" -template::value && !std::is_same::value>::type> -bool extract(const_string str, T *iv) +template::value && !std::is_same::value && !std::is_same::value>::type> +bool extract(XString str, T *iv) { if (!str || str.size() > 20) return false; if (!((str.front() == '-' && std::is_signed::value) || ('0' <= str.front() && str.front() <= '9'))) return false; + // needs a NUL, but can't always be given one. TODO VString? char buf[20 + 1]; std::copy(str.begin(), str.end(), buf); buf[str.size()] = '\0'; @@ -60,14 +61,14 @@ bool extract(const_string str, T *iv) } inline -bool extract(const_string str, TimeT *tv) +bool extract(XString str, TimeT *tv) { return extract(str, &tv->value); } // extra typename=void to workaround some duplicate overload rule template::value>::type, typename=void> -bool extract(const_string str, T *iv) +bool extract(XString str, T *iv) { typedef typename underlying_type::type U; U v; @@ -79,59 +80,90 @@ bool extract(const_string str, T *iv) return true; } -bool extract(const_string str, const_string *rv); +bool extract(XString str, XString *rv); -bool extract(const_string str, std::string *rv); +bool extract(XString str, FString *rv); -template -__attribute__((deprecated)) -bool extract(const_string str, char (*out)[N]) +template +bool extract(XString str, VString *out) { - if (str.size() >= N) + if (str.size() > N) return false; - std::copy(str.begin(), str.end(), *out); - std::fill(*out + str.size() , *out + N, '\0'); + *out = str; return true; } +template +class LStripper +{ +public: + T impl; +}; + +template +LStripper lstripping(T v) +{ + return {v}; +} + +template +bool extract(XString str, LStripper out) +{ + return extract(str.lstrip(), out.impl); +} + // basically just a std::tuple // but it provides its data members publically -template +template class Record; -template -class Record +template +class Record { }; -template -class Record +template +class Record { public: F frist; - Record rest; + Record rest; public: Record(F f, R... r) : frist(f), rest(r...) {} }; template -Record record(T... t) +Record record(T... t) +{ + return Record(t...); +} +template +Record record(T... t) { - return Record(t...); + static_assert(0 < n && n < sizeof...(T), "don't be silly"); + return Record(t...); } -template -bool extract(const_string str, Record) +template +bool extract(XString str, Record) { return !str; } -template -bool extract(const_string str, Record rec) + +template +bool extract(XString str, Record rec) { - const char *s = std::find(str.begin(), str.end(), split); + XString::iterator s = std::find(str.begin(), str.end(), split); + XString::iterator s2 = s; + if (s2 != str.end()) + ++s2; + XString head = str.xislice_h(s); + XString tail = str.xislice_t(s2); if (s == str.end()) - return sizeof...(R) == 0 && extract(str, rec.frist); - return extract(const_string(str.begin(), s), rec.frist) - && extract(const_string(s + 1, str.end()), rec.rest); + return (extract(head, rec.frist) && n <= 1) + || (!head && n <= 0); + + return (extract(head, rec.frist) || n <= 0) + && extract(tail, rec.rest); } template @@ -147,20 +179,41 @@ VRecord vrec(std::vector *arr) } template -bool extract(const_string str, VRecord rec) +bool extract(XString str, VRecord rec) { - if (str.empty()) + if (!str) return true; - const char *s = std::find(str.begin(), str.end(), split); + XString::iterator s = std::find(str.begin(), str.end(), split); rec.arr->emplace_back(); if (s == str.end()) return extract(str, &rec.arr->back()); - return extract(const_string(str.begin(), s), &rec.arr->back()) - && extract(const_string(s + 1, str.end()), rec); + return extract(str.xislice_h(s), &rec.arr->back()) + && extract(str.xislice_t(s + 1), rec); } -bool extract(const_string str, struct global_reg *var); +bool extract(XString str, struct global_reg *var); + +bool extract(XString str, struct item *it); + +inline +bool extract(XString str, MapName *m) +{ + VString<15> tmp; + bool rv = extract(str, &tmp); + *m = tmp; + return rv; +} -bool extract(const_string str, struct item *it); +inline +bool extract(XString str, CharName *out) +{ + VString<23> tmp; + if (extract(str, &tmp)) + { + *out = CharName(tmp); + return true; + } + return false; +} #endif // EXTRACT_HPP diff --git a/src/common/extract_test.cpp b/src/common/extract_test.cpp new file mode 100644 index 0000000..d8e9ebe --- /dev/null +++ b/src/common/extract_test.cpp @@ -0,0 +1,319 @@ +#include "extract.hpp" + +#include + +TEST(extract, record_int) +{ + int x, y, z; + x = y = z = 0; + EXPECT_FALSE(extract("1 2 3 4 ", record<' '>(&x, &y, &z))); + EXPECT_EQ(1, x); + EXPECT_EQ(2, y); + EXPECT_EQ(3, z); + x = y = z = 0; + EXPECT_FALSE(extract("1 2 3 4", record<' '>(&x, &y, &z))); + EXPECT_EQ(1, x); + EXPECT_EQ(2, y); + EXPECT_EQ(3, z); + x = y = z = 0; + EXPECT_TRUE(extract("1 2 3 ", record<' '>(&x, &y, &z))); + EXPECT_EQ(1, x); + EXPECT_EQ(2, y); + EXPECT_EQ(3, z); + x = y = z = 0; + EXPECT_TRUE(extract("1 2 3", record<' '>(&x, &y, &z))); + EXPECT_EQ(1, x); + EXPECT_EQ(2, y); + EXPECT_EQ(3, z); + x = y = z = 0; + EXPECT_FALSE(extract("1 2 ", record<' '>(&x, &y, &z))); + EXPECT_EQ(1, x); + EXPECT_EQ(2, y); + EXPECT_EQ(0, z); + x = y = z = 0; + EXPECT_FALSE(extract("1 2", record<' '>(&x, &y, &z))); + EXPECT_EQ(1, x); + EXPECT_EQ(2, y); + EXPECT_EQ(0, z); + x = y = z = 0; + EXPECT_FALSE(extract("1 ", record<' '>(&x, &y, &z))); + EXPECT_EQ(1, x); + EXPECT_EQ(0, y); + EXPECT_EQ(0, z); + x = y = z = 0; + EXPECT_FALSE(extract("1", record<' '>(&x, &y, &z))); + EXPECT_EQ(1, x); + EXPECT_EQ(0, y); + EXPECT_EQ(0, z); + x = y = z = 0; + EXPECT_FALSE(extract(" ", record<' '>(&x, &y, &z))); + EXPECT_EQ(0, x); + EXPECT_EQ(0, y); + EXPECT_EQ(0, z); + x = y = z = 0; + EXPECT_FALSE(extract("", record<' '>(&x, &y, &z))); + EXPECT_EQ(0, x); + EXPECT_EQ(0, y); + EXPECT_EQ(0, z); + x = y = z = 0; + + EXPECT_FALSE(extract("1 2 3 4 ", record<' ', 2>(&x, &y, &z))); + EXPECT_EQ(1, x); + EXPECT_EQ(2, y); + EXPECT_EQ(3, z); + x = y = z = 0; + EXPECT_FALSE(extract("1 2 3 4", record<' ', 2>(&x, &y, &z))); + EXPECT_EQ(1, x); + EXPECT_EQ(2, y); + EXPECT_EQ(3, z); + x = y = z = 0; + EXPECT_TRUE(extract("1 2 3 ", record<' ', 2>(&x, &y, &z))); + EXPECT_EQ(1, x); + EXPECT_EQ(2, y); + EXPECT_EQ(3, z); + x = y = z = 0; + EXPECT_TRUE(extract("1 2 3", record<' ', 2>(&x, &y, &z))); + EXPECT_EQ(1, x); + EXPECT_EQ(2, y); + EXPECT_EQ(3, z); + x = y = z = 0; + EXPECT_TRUE(extract("1 2 ", record<' ', 2>(&x, &y, &z))); + EXPECT_EQ(1, x); + EXPECT_EQ(2, y); + EXPECT_EQ(0, z); + x = y = z = 0; + EXPECT_TRUE(extract("1 2", record<' ', 2>(&x, &y, &z))); + EXPECT_EQ(1, x); + EXPECT_EQ(2, y); + EXPECT_EQ(0, z); + x = y = z = 0; + EXPECT_FALSE(extract("1 ", record<' ', 2>(&x, &y, &z))); + EXPECT_EQ(1, x); + EXPECT_EQ(0, y); + EXPECT_EQ(0, z); + x = y = z = 0; + EXPECT_FALSE(extract("1", record<' ', 2>(&x, &y, &z))); + EXPECT_EQ(1, x); + EXPECT_EQ(0, y); + EXPECT_EQ(0, z); + x = y = z = 0; + EXPECT_FALSE(extract(" ", record<' ', 2>(&x, &y, &z))); + EXPECT_EQ(0, x); + EXPECT_EQ(0, y); + EXPECT_EQ(0, z); + x = y = z = 0; + EXPECT_FALSE(extract("", record<' ', 2>(&x, &y, &z))); + EXPECT_EQ(0, x); + EXPECT_EQ(0, y); + EXPECT_EQ(0, z); + x = y = z = 0; + + EXPECT_FALSE(extract("1 2 3 4 ", record<' ', 1>(&x, &y, &z))); + EXPECT_EQ(1, x); + EXPECT_EQ(2, y); + EXPECT_EQ(3, z); + x = y = z = 0; + EXPECT_FALSE(extract("1 2 3 4", record<' ', 1>(&x, &y, &z))); + EXPECT_EQ(1, x); + EXPECT_EQ(2, y); + EXPECT_EQ(3, z); + x = y = z = 0; + EXPECT_TRUE(extract("1 2 3 ", record<' ', 1>(&x, &y, &z))); + EXPECT_EQ(1, x); + EXPECT_EQ(2, y); + EXPECT_EQ(3, z); + x = y = z = 0; + EXPECT_TRUE(extract("1 2 3", record<' ', 1>(&x, &y, &z))); + EXPECT_EQ(1, x); + EXPECT_EQ(2, y); + EXPECT_EQ(3, z); + x = y = z = 0; + EXPECT_TRUE(extract("1 2 ", record<' ', 1>(&x, &y, &z))); + EXPECT_EQ(1, x); + EXPECT_EQ(2, y); + EXPECT_EQ(0, z); + x = y = z = 0; + EXPECT_TRUE(extract("1 2", record<' ', 1>(&x, &y, &z))); + EXPECT_EQ(1, x); + EXPECT_EQ(2, y); + EXPECT_EQ(0, z); + x = y = z = 0; + EXPECT_TRUE(extract("1 ", record<' ', 1>(&x, &y, &z))); + EXPECT_EQ(1, x); + EXPECT_EQ(0, y); + EXPECT_EQ(0, z); + x = y = z = 0; + EXPECT_TRUE(extract("1", record<' ', 1>(&x, &y, &z))); + EXPECT_EQ(1, x); + EXPECT_EQ(0, y); + EXPECT_EQ(0, z); + x = y = z = 0; + EXPECT_FALSE(extract(" ", record<' ', 1>(&x, &y, &z))); + EXPECT_EQ(0, x); + EXPECT_EQ(0, y); + EXPECT_EQ(0, z); + x = y = z = 0; + EXPECT_FALSE(extract("", record<' ', 1>(&x, &y, &z))); + EXPECT_EQ(0, x); + EXPECT_EQ(0, y); + EXPECT_EQ(0, z); + x = y = z = 0; +} + +TEST(extract, record_str) +{ + XString x, y, z; + x = y = z = ""; + EXPECT_FALSE(extract("1 2 3 4 ", record<' '>(&x, &y, &z))); + EXPECT_EQ("1", x); + EXPECT_EQ("2", y); + EXPECT_EQ("3", z); + x = y = z = ""; + EXPECT_FALSE(extract("1 2 3 4", record<' '>(&x, &y, &z))); + EXPECT_EQ("1", x); + EXPECT_EQ("2", y); + EXPECT_EQ("3", z); + x = y = z = ""; + EXPECT_TRUE(extract("1 2 3 ", record<' '>(&x, &y, &z))); + EXPECT_EQ("1", x); + EXPECT_EQ("2", y); + EXPECT_EQ("3", z); + x = y = z = ""; + EXPECT_TRUE(extract("1 2 3", record<' '>(&x, &y, &z))); + EXPECT_EQ("1", x); + EXPECT_EQ("2", y); + EXPECT_EQ("3", z); + x = y = z = ""; + EXPECT_TRUE(extract("1 2 ", record<' '>(&x, &y, &z))); + EXPECT_EQ("1", x); + EXPECT_EQ("2", y); + EXPECT_EQ("", z); + x = y = z = ""; + EXPECT_FALSE(extract("1 2", record<' '>(&x, &y, &z))); + EXPECT_EQ("1", x); + EXPECT_EQ("2", y); + EXPECT_EQ("", z); + x = y = z = ""; + EXPECT_FALSE(extract("1 ", record<' '>(&x, &y, &z))); + EXPECT_EQ("1", x); + EXPECT_EQ("", y); + EXPECT_EQ("", z); + x = y = z = ""; + EXPECT_FALSE(extract("1", record<' '>(&x, &y, &z))); + EXPECT_EQ("1", x); + EXPECT_EQ("", y); + EXPECT_EQ("", z); + x = y = z = ""; + EXPECT_FALSE(extract(" ", record<' '>(&x, &y, &z))); + EXPECT_EQ("", x); + EXPECT_EQ("", y); + EXPECT_EQ("", z); + x = y = z = ""; + EXPECT_FALSE(extract("", record<' '>(&x, &y, &z))); + EXPECT_EQ("", x); + EXPECT_EQ("", y); + EXPECT_EQ("", z); + x = y = z = ""; + + EXPECT_FALSE(extract("1 2 3 4 ", record<' ', 2>(&x, &y, &z))); + EXPECT_EQ("1", x); + EXPECT_EQ("2", y); + EXPECT_EQ("3", z); + x = y = z = ""; + EXPECT_FALSE(extract("1 2 3 4", record<' ', 2>(&x, &y, &z))); + EXPECT_EQ("1", x); + EXPECT_EQ("2", y); + EXPECT_EQ("3", z); + x = y = z = ""; + EXPECT_TRUE(extract("1 2 3 ", record<' ', 2>(&x, &y, &z))); + EXPECT_EQ("1", x); + EXPECT_EQ("2", y); + EXPECT_EQ("3", z); + x = y = z = ""; + EXPECT_TRUE(extract("1 2 3", record<' ', 2>(&x, &y, &z))); + EXPECT_EQ("1", x); + EXPECT_EQ("2", y); + EXPECT_EQ("3", z); + x = y = z = ""; + EXPECT_TRUE(extract("1 2 ", record<' ', 2>(&x, &y, &z))); + EXPECT_EQ("1", x); + EXPECT_EQ("2", y); + EXPECT_EQ("", z); + x = y = z = ""; + EXPECT_TRUE(extract("1 2", record<' ', 2>(&x, &y, &z))); + EXPECT_EQ("1", x); + EXPECT_EQ("2", y); + EXPECT_EQ("", z); + x = y = z = ""; + EXPECT_TRUE(extract("1 ", record<' ', 2>(&x, &y, &z))); + EXPECT_EQ("1", x); + EXPECT_EQ("", y); + EXPECT_EQ("", z); + x = y = z = ""; + EXPECT_FALSE(extract("1", record<' ', 2>(&x, &y, &z))); + EXPECT_EQ("1", x); + EXPECT_EQ("", y); + EXPECT_EQ("", z); + x = y = z = ""; + EXPECT_TRUE(extract(" ", record<' ', 2>(&x, &y, &z))); + EXPECT_EQ("", x); + EXPECT_EQ("", y); + EXPECT_EQ("", z); + x = y = z = ""; + EXPECT_FALSE(extract("", record<' ', 2>(&x, &y, &z))); + EXPECT_EQ("", x); + EXPECT_EQ("", y); + EXPECT_EQ("", z); + x = y = z = ""; + + EXPECT_FALSE(extract("1 2 3 4 ", record<' ', 1>(&x, &y, &z))); + EXPECT_EQ("1", x); + EXPECT_EQ("2", y); + EXPECT_EQ("3", z); + x = y = z = ""; + EXPECT_FALSE(extract("1 2 3 4", record<' ', 1>(&x, &y, &z))); + EXPECT_EQ("1", x); + EXPECT_EQ("2", y); + EXPECT_EQ("3", z); + x = y = z = ""; + EXPECT_TRUE(extract("1 2 3 ", record<' ', 1>(&x, &y, &z))); + EXPECT_EQ("1", x); + EXPECT_EQ("2", y); + EXPECT_EQ("3", z); + x = y = z = ""; + EXPECT_TRUE(extract("1 2 3", record<' ', 1>(&x, &y, &z))); + EXPECT_EQ("1", x); + EXPECT_EQ("2", y); + EXPECT_EQ("3", z); + x = y = z = ""; + EXPECT_TRUE(extract("1 2 ", record<' ', 1>(&x, &y, &z))); + EXPECT_EQ("1", x); + EXPECT_EQ("2", y); + EXPECT_EQ("", z); + x = y = z = ""; + EXPECT_TRUE(extract("1 2", record<' ', 1>(&x, &y, &z))); + EXPECT_EQ("1", x); + EXPECT_EQ("2", y); + EXPECT_EQ("", z); + x = y = z = ""; + EXPECT_TRUE(extract("1 ", record<' ', 1>(&x, &y, &z))); + EXPECT_EQ("1", x); + EXPECT_EQ("", y); + EXPECT_EQ("", z); + x = y = z = ""; + EXPECT_TRUE(extract("1", record<' ', 1>(&x, &y, &z))); + EXPECT_EQ("1", x); + EXPECT_EQ("", y); + EXPECT_EQ("", z); + x = y = z = ""; + EXPECT_TRUE(extract(" ", record<' ', 1>(&x, &y, &z))); + EXPECT_EQ("", x); + EXPECT_EQ("", y); + EXPECT_EQ("", z); + x = y = z = ""; + EXPECT_TRUE(extract("", record<' ', 1>(&x, &y, &z))); + EXPECT_EQ("", x); + EXPECT_EQ("", y); + EXPECT_EQ("", z); + x = y = z = ""; +} diff --git a/src/common/human_time_diff.hpp b/src/common/human_time_diff.hpp new file mode 100644 index 0000000..3fc0f09 --- /dev/null +++ b/src/common/human_time_diff.hpp @@ -0,0 +1,85 @@ +#ifndef TMWA_COMMON_HUMAN_TIME_DIFF_HPP +#define TMWA_COMMON_HUMAN_TIME_DIFF_HPP +// human_time_diff.hpp - broken deltas +// +// Copyright © 2013 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 "sanity.hpp" + +#include "extract.hpp" +#include "strings.hpp" + +struct HumanTimeDiff +{ + short year, month, day, hour, minute, second; + + explicit + operator bool() + { + return year || month || day || hour || minute || second; + } + + bool operator !() + { + return !bool(*this); + } +}; + +inline +bool extract(XString str, HumanTimeDiff *iv) +{ + // str is a sequence of [-+]?[0-9]+([ay]|m|[jd]|h|mn|s) + // there are NO spaces here + // parse by counting the number starts + auto is_num = [](char c) + { return c == '-' || c == '+' || ('0' <= c && c <= '9'); }; + if (!str || !is_num(str.front())) + return false; + *iv = HumanTimeDiff{}; + while (str) + { + auto it = std::find_if_not(str.begin(), str.end(), is_num); + auto it2 = std::find_if(it, str.end(), is_num); + XString number = str.xislice_h(it); + XString suffix = str.xislice(it, it2); + str = str.xislice_t(it2); + + short *ptr = nullptr; + if (suffix == "y" || suffix == "a") + ptr = &iv->year; + else if (suffix == "m") + ptr = &iv->month; + else if (suffix == "j" || suffix == "d") + ptr = &iv->day; + else if (suffix == "h") + ptr = &iv->hour; + else if (suffix == "mn") + ptr = &iv->minute; + else if (suffix == "s") + ptr = &iv->second; + else + return false; + if (number.startswith('+') && !number.startswith("+-")) + number = number.xslice_t(1); + if (*ptr || !extract(number, ptr)) + return false; + } + return true; +} + +#endif // TMWA_COMMON_HUMAN_TIME_DIFF_HPP diff --git a/src/common/human_time_diff_test.cpp b/src/common/human_time_diff_test.cpp new file mode 100644 index 0000000..d11a116 --- /dev/null +++ b/src/common/human_time_diff_test.cpp @@ -0,0 +1,84 @@ +#include "human_time_diff.hpp" + +#include + +// a sequence of [-+]?[0-9]+([ay]|m|[jd]|h|mn|s) + +TEST(humantimediff, single) +{ + HumanTimeDiff diff; + + EXPECT_TRUE(extract("42y", &diff)); + EXPECT_EQ(42, diff.year); + EXPECT_EQ(0, diff.month); + EXPECT_EQ(0, diff.day); + EXPECT_EQ(0, diff.hour); + EXPECT_EQ(0, diff.minute); + EXPECT_EQ(0, diff.second); + + EXPECT_TRUE(extract("42m", &diff)); + EXPECT_EQ(0, diff.year); + EXPECT_EQ(42, diff.month); + EXPECT_EQ(0, diff.day); + EXPECT_EQ(0, diff.hour); + EXPECT_EQ(0, diff.minute); + EXPECT_EQ(0, diff.second); + + EXPECT_TRUE(extract("42d", &diff)); + EXPECT_EQ(0, diff.year); + EXPECT_EQ(0, diff.month); + EXPECT_EQ(42, diff.day); + EXPECT_EQ(0, diff.hour); + EXPECT_EQ(0, diff.minute); + EXPECT_EQ(0, diff.second); + + EXPECT_TRUE(extract("42h", &diff)); + EXPECT_EQ(0, diff.year); + EXPECT_EQ(0, diff.month); + EXPECT_EQ(0, diff.day); + EXPECT_EQ(42, diff.hour); + EXPECT_EQ(0, diff.minute); + EXPECT_EQ(0, diff.second); + + EXPECT_TRUE(extract("42mn", &diff)); + EXPECT_EQ(0, diff.year); + EXPECT_EQ(0, diff.month); + EXPECT_EQ(0, diff.day); + EXPECT_EQ(0, diff.hour); + EXPECT_EQ(42, diff.minute); + EXPECT_EQ(0, diff.second); + + EXPECT_TRUE(extract("42s", &diff)); + EXPECT_EQ(0, diff.year); + EXPECT_EQ(0, diff.month); + EXPECT_EQ(0, diff.day); + EXPECT_EQ(0, diff.hour); + EXPECT_EQ(0, diff.minute); + EXPECT_EQ(42, diff.second); + + EXPECT_TRUE(extract("+42y", &diff)); + EXPECT_EQ(42, diff.year); + EXPECT_TRUE(extract("-42y", &diff)); + EXPECT_EQ(-42, diff.year); + EXPECT_FALSE(extract("++42y", &diff)); + EXPECT_FALSE(extract("+-42y", &diff)); + EXPECT_FALSE(extract("-+42y", &diff)); + EXPECT_FALSE(extract("--42y", &diff)); + EXPECT_FALSE(extract("4+2y", &diff)); + EXPECT_FALSE(extract("42z", &diff)); +} + +TEST(humantimediff, multiple) +{ + HumanTimeDiff diff; + + EXPECT_TRUE(extract("42y23m-2d", &diff)); + EXPECT_EQ(42, diff.year); + EXPECT_EQ(23, diff.month); + EXPECT_EQ(-2, diff.day); + EXPECT_EQ(0, diff.hour); + EXPECT_EQ(0, diff.minute); + EXPECT_EQ(0, diff.second); + EXPECT_FALSE(extract("1y2y", &diff)); +} + diff --git a/src/common/intern-pool.hpp b/src/common/intern-pool.hpp index caa54e4..db840a2 100644 --- a/src/common/intern-pool.hpp +++ b/src/common/intern-pool.hpp @@ -4,16 +4,19 @@ #include #include -#include #include +#include "strings.hpp" + class InternPool { - std::map known; - std::vector names; + std::map known; + std::vector names; public: - size_t intern(const std::string& name) + size_t intern(XString name_) { + FString name = name_; + // hm, I could change this to do aliases auto pair = known.insert({name, known.size()}); if (pair.second) names.push_back(name); @@ -21,7 +24,7 @@ public: return pair.first->second; } - const std::string& outtern(size_t sz) const + ZString outtern(size_t sz) const { return names[sz]; } diff --git a/src/common/intern-pool_test.cpp b/src/common/intern-pool_test.cpp new file mode 100644 index 0000000..3bbca7b --- /dev/null +++ b/src/common/intern-pool_test.cpp @@ -0,0 +1,18 @@ +#include "intern-pool.hpp" + +#include + +TEST(InternPool, whydoesthisalwaysneedasecondname) +{ + InternPool p; + EXPECT_EQ(0, p.size()); + EXPECT_EQ(0, p.intern("hello")); + EXPECT_EQ(0, p.intern("hello")); + EXPECT_EQ(1, p.size()); + EXPECT_EQ(1, p.intern("world")); + EXPECT_EQ(0, p.intern("hello")); + EXPECT_EQ(1, p.intern("world")); + EXPECT_EQ(2, p.size()); + EXPECT_EQ("hello", p.outtern(0)); + EXPECT_EQ("world", p.outtern(1)); +} diff --git a/src/common/io.hpp b/src/common/io.hpp new file mode 100644 index 0000000..1831651 --- /dev/null +++ b/src/common/io.hpp @@ -0,0 +1,26 @@ +#ifndef TMWA_COMMON_IO_HPP +#define TMWA_COMMON_IO_HPP + +#include +#include + +#include "strings.hpp" + +namespace io +{ + inline + std::istream& getline(std::istream& in, FString& line) + { + std::string s; + if (std::getline(in, s)) + { + std::string::const_iterator begin = s.cbegin(), end = s.cend(); + if (begin != end && end[-1] == '\r') + --end; + line = FString(begin, end); + } + return in; + } +} // namespace io + +#endif //TMWA_COMMON_IO_HPP diff --git a/src/common/lock.cpp b/src/common/lock.cpp index 82856e1..f19cd92 100644 --- a/src/common/lock.cpp +++ b/src/common/lock.cpp @@ -17,17 +17,17 @@ const int backup_count = 10; /// (Until the file is closed, it keeps the old file) // Start writing a tmpfile -FILE *lock_fopen(const char *filename, int *info) +FILE *lock_fopen(ZString filename, int *info) { FILE *fp; int no = getpid(); // Get a filename that doesn't already exist - std::string newfile; + FString newfile; do { newfile = STRPRINTF("%s_%d.tmp", filename, no++); - fp = fopen_(newfile.c_str(), "wx"); + fp = fopen(newfile.c_str(), "wx"); } while (!fp); *info = --no; @@ -35,22 +35,22 @@ FILE *lock_fopen(const char *filename, int *info) } // Delete the old file and rename the new file -void lock_fclose(FILE *fp, const char *filename, int *info) +void lock_fclose(FILE *fp, ZString filename, int *info) { if (fp) { - fclose_(fp); + fclose(fp); int n = backup_count; - std::string old_filename = STRPRINTF("%s.%d", filename, n); + FString old_filename = STRPRINTF("%s.%d", filename, n); while (--n) { - std::string newer_filename = STRPRINTF("%s.%d", filename, n); + FString newer_filename = STRPRINTF("%s.%d", filename, n); rename(newer_filename.c_str(), old_filename.c_str()); old_filename = std::move(newer_filename); } - rename(filename, old_filename.c_str()); + rename(filename.c_str(), old_filename.c_str()); - std::string tmpfile = STRPRINTF("%s_%d.tmp", filename, *info); - rename(tmpfile.c_str(), filename); + FString tmpfile = STRPRINTF("%s_%d.tmp", filename, *info); + rename(tmpfile.c_str(), filename.c_str()); } } diff --git a/src/common/lock.hpp b/src/common/lock.hpp index f7ce2d8..df4d1f8 100644 --- a/src/common/lock.hpp +++ b/src/common/lock.hpp @@ -5,11 +5,13 @@ #include +#include "strings.hpp" + // TODO replace with a class /// Locked FILE I/O // Changes are made in a separate file until lock_fclose -FILE *lock_fopen(const char *filename, int *info); -void lock_fclose(FILE * fp, const char *filename, int *info); +FILE *lock_fopen(ZString filename, int *info); +void lock_fclose(FILE *fp, ZString filename, int *info); #endif // LOCK_HPP diff --git a/src/common/md5calc.cpp b/src/common/md5calc.cpp index 582c152..ae134b7 100644 --- a/src/common/md5calc.cpp +++ b/src/common/md5calc.cpp @@ -2,6 +2,7 @@ #include +#include "cxxstdio.hpp" #include "random.hpp" #include "utils.hpp" @@ -171,7 +172,7 @@ void MD5_do_block(MD5_state* state, MD5_block block) #undef d } -void MD5_to_bin(MD5_state state, uint8_t out[0x10]) +void MD5_to_bin(MD5_state state, md5_binary& out) { for (int i = 0; i < 0x10; i++) out[i] = state.val[i / 4] >> 8 * (i % 4); @@ -180,47 +181,47 @@ void MD5_to_bin(MD5_state state, uint8_t out[0x10]) static const char hex[] = "0123456789abcdef"; -void MD5_to_str(MD5_state state, char out[0x21]) +void MD5_to_str(MD5_state state, md5_string& out_) { - uint8_t bin[16]; + md5_binary bin; MD5_to_bin(state, bin); + char out[0x20]; for (int i = 0; i < 0x10; i++) out[2 * i] = hex[bin[i] >> 4], out[2 * i + 1] = hex[bin[i] & 0xf]; - out[0x20] = '\0'; + out_ = stringish(XString(out, out + 0x20, nullptr)); } -MD5_state MD5_from_string(const char* msg, const size_t msglen) +MD5_state MD5_from_string(XString msg) { MD5_state state; MD5_init(&state); MD5_block block; - size_t rem = msglen; - while (rem >= 64) + const uint64_t msg_full_len = msg.size(); + while (msg.size() >= 64) { for (int i = 0; i < 0x10; i++) X[i] = msg[4 * i + 0] | msg[4 * i + 1] << 8 | msg[4 * i + 2] << 16 | msg[4 * i + 3] << 24; MD5_do_block(&state, block); - msg += 64; - rem -= 64; + msg = msg.xslice_t(64); } // now pad 1-512 bits + the 64-bit length - may be two blocks uint8_t buf[0x40] = {}; - really_memcpy(buf, reinterpret_cast(msg), rem); - buf[rem] = 0x80; // a single one bit - if (64 - rem > 8) + really_memcpy(buf, reinterpret_cast(msg.data()), msg.size()); + buf[msg.size()] = 0x80; // a single one bit + if (64 - msg.size() > 8) { for (int i = 0; i < 8; i++) - buf[0x38 + i] = (static_cast(msglen) * 8) >> (i * 8); + buf[0x38 + i] = (msg_full_len * 8) >> (i * 8); } for (int i = 0; i < 0x10; i++) X[i] = buf[4 * i + 0] | buf[4 * i + 1] << 8 | buf[4 * i + 2] << 16 | buf[4 * i + 3] << 24; MD5_do_block(&state, block); - if (64 - rem <= 8) + if (64 - msg.size() <= 8) { really_memset0(buf, 0x38); for (int i = 0; i < 8; i++) - buf[0x38 + i] = (static_cast(msglen) * 8) >> (i * 8); + buf[0x38 + i] = (msg_full_len * 8) >> (i * 8); for (int i = 0; i < 0x10; i++) X[i] = buf[4 * i + 0] | buf[4 * i + 1] << 8 | buf[4 * i + 2] << 16 | buf[4 * i + 3] << 24; MD5_do_block(&state, block); @@ -228,12 +229,8 @@ MD5_state MD5_from_string(const char* msg, const size_t msglen) return state; } -// This could be reimplemented without the strlen() -MD5_state MD5_from_cstring(const char* msg) -{ - return MD5_from_string(msg, strlen(msg)); -} - +// TODO - refactor MD5 into a stream, and merge the implementations +// I once implemented an ostream that does it ... MD5_state MD5_from_FILE(FILE* in) { uint64_t total_len = 0; @@ -286,50 +283,53 @@ MD5_state MD5_from_FILE(FILE* in) // Hash a password with a salt. // Whoever wrote this FAILS programming -const char *MD5_saltcrypt(const char *key, const char *salt) +AccountCrypt MD5_saltcrypt(AccountPass key, SaltString salt) { - char buf[65]; + char cbuf[64] {}; // hash the key then the salt // buf ends up as a 64-char NUL-terminated string - MD5_to_str(MD5_from_cstring(key), buf); - MD5_to_str(MD5_from_cstring(salt), buf + 32); + md5_string tbuf, tbuf2; + MD5_to_str(MD5_from_string(key), tbuf); + MD5_to_str(MD5_from_string(salt), tbuf2); + const auto it = std::copy(tbuf.begin(), tbuf.end(), std::begin(cbuf)); + auto it2 = std::copy(tbuf2.begin(), tbuf2.end(), it); + assert(it2 == std::end(cbuf)); + + md5_string tbuf3; + MD5_to_str(MD5_from_string(XString(std::begin(cbuf), it2, nullptr)), tbuf3); - // Hash the buffer back into sbuf - this is stupid - // (luckily, putting the result into itself is safe) - MD5_to_str(MD5_from_cstring(buf), buf + 32); + VString<31> obuf; - static char obuf[33]; // This truncates the string, but we have to keep it like that for compatibility - snprintf(obuf, 32, "!%s$%s", salt, buf + 32); - return obuf; + SNPRINTF(obuf, 32, "!%s$%s", salt, tbuf3); + return stringish(obuf); } -const char *make_salt(void) +SaltString make_salt(void) { - static char salt[6]; + char salt[5]; for (int i = 0; i < 5; i++) // 126 would probably actually be okay salt[i] = random_::in(48, 125); - return salt; + return stringish(XString(salt + 0, salt + 5, nullptr)); } -bool pass_ok(const char *password, const char *crypted) +bool pass_ok(AccountPass password, AccountCrypt crypted) { - char buf[40]; - strzcpy(buf, crypted, 40); // crypted is like !salt$hash - char *salt = buf + 1; - *strchr(salt, '$') = '\0'; + auto begin = crypted.begin() + 1; + auto end = std::find(begin, crypted.end(), '$'); + SaltString salt = stringish(crypted.xislice(begin, end)); - return !strcmp(crypted, MD5_saltcrypt(password, salt)); + return crypted == MD5_saltcrypt(password, salt); } // [M|h]ashes up an IP address and a secret key // to return a hopefully unique masked IP. -struct in_addr MD5_ip(char *secret, struct in_addr ip) +struct in_addr MD5_ip(struct in_addr ip) { - uint8_t obuf[16]; + static SaltString secret = make_salt(); union { uint8_t bytes[4]; @@ -337,10 +337,10 @@ struct in_addr MD5_ip(char *secret, struct in_addr ip) } conv; // MD5sum a secret + the IP address - char ipbuf[32] {}; - snprintf(ipbuf, sizeof(ipbuf), "%u%s", ip.s_addr, secret); - /// TODO stop it from being a cstring - MD5_to_bin(MD5_from_cstring(ipbuf), obuf); + VString<31> ipbuf; + SNPRINTF(ipbuf, 32, "%u%s", ip.s_addr, secret); + md5_binary obuf; + MD5_to_bin(MD5_from_string(ipbuf), obuf); // Fold the md5sum to 32 bits, pack the bytes to an in_addr conv.bytes[0] = obuf[0] ^ obuf[1] ^ obuf[8] ^ obuf[9]; diff --git a/src/common/md5calc.hpp b/src/common/md5calc.hpp index de19e0f..98f44d6 100644 --- a/src/common/md5calc.hpp +++ b/src/common/md5calc.hpp @@ -9,44 +9,51 @@ #include #include +#include + +#include "mmo.hpp" +#include "strings.hpp" + /// The digest state - becomes the output -typedef struct +struct MD5_state { // classically named {A,B,C,D} // but use an so we can index uint32_t val[4]; -} MD5_state; -typedef struct +}; +struct MD5_block { uint32_t data[16]; -} MD5_block; +}; + +struct md5_binary : std::array {}; +struct md5_string : VString<0x20> {}; +struct SaltString : VString<5> {}; // Implementation -void MD5_init(MD5_state* state); -void MD5_do_block(MD5_state* state, MD5_block block); +void MD5_init(MD5_state *state); +void MD5_do_block(MD5_state *state, MD5_block block); // Output formatting -void MD5_to_bin(MD5_state state, uint8_t out[0x10]); -void MD5_to_str(MD5_state state, char out[0x21]); +void MD5_to_bin(MD5_state state, md5_binary& out); +void MD5_to_str(MD5_state state, md5_string& out); // Convenience -MD5_state MD5_from_string(const char* msg, const size_t msglen); -MD5_state MD5_from_cstring(const char* msg); +MD5_state MD5_from_string(XString msg); MD5_state MD5_from_FILE(FILE* in); -// statically-allocated output // whoever wrote this fails basic understanding of -const char *MD5_saltcrypt(const char *key, const char *salt); +AccountCrypt MD5_saltcrypt(AccountPass key, SaltString salt); /// return some random characters (statically allocated) // Currently, returns a 5-char string -const char *make_salt(void); +SaltString make_salt(void); /// check plaintext password against saved saltcrypt -bool pass_ok(const char *password, const char *crypted); +bool pass_ok(AccountPass password, AccountCrypt crypted); /// This returns an in_addr because it is configurable whether it gets called at all -struct in_addr MD5_ip(char *secret, struct in_addr ip); +struct in_addr MD5_ip(struct in_addr ip); #endif // MD5CALC_HPP diff --git a/src/common/md5calc_test.cpp b/src/common/md5calc_test.cpp new file mode 100644 index 0000000..51b0b68 --- /dev/null +++ b/src/common/md5calc_test.cpp @@ -0,0 +1,27 @@ +#include "md5calc.hpp" + +#include + +#include "utils.hpp" + +// This should be made part of the main API, +// but is not yet to keep the diff small. +// Edit: hack to fix the new strict comparison. +static +VString<32> MD5(XString in) +{ + md5_string out; + MD5_to_str(MD5_from_string(in), out); + return out; +} + +TEST(md5calc, rfc1321) +{ + EXPECT_EQ("d41d8cd98f00b204e9800998ecf8427e", MD5("")); + EXPECT_EQ("0cc175b9c0f1b6a831c399e269772661", MD5("a")); + EXPECT_EQ("900150983cd24fb0d6963f7d28e17f72", MD5("abc")); + EXPECT_EQ("f96b697d7cb7938d525a2f31aaf161d0", MD5("message digest")); + EXPECT_EQ("c3fcd3d76192e4007dfb496cca67e13b", MD5("abcdefghijklmnopqrstuvwxyz")); + EXPECT_EQ("d174ab98d277d9f5a5611c2c9f419d9f", MD5("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")); + EXPECT_EQ("57edf4a22be3c955ac49da2e2107b67a", MD5("12345678901234567890123456789012345678901234567890123456789012345678901234567890")); +} diff --git a/src/common/mmo.hpp b/src/common/mmo.hpp index 450aa61..89ff50a 100644 --- a/src/common/mmo.hpp +++ b/src/common/mmo.hpp @@ -4,7 +4,10 @@ # include "sanity.hpp" # include "timer.t.hpp" -# include "utils.hpp" +# include "utils2.hpp" + +// affects CharName +# define NAME_IGNORING_CASE 1 constexpr int FIFOSIZE_SERVERLINK = 256 * 1024; @@ -37,6 +40,105 @@ constexpr int MAX_PARTY = 12; # define CHAR_CONF_NAME "conf/char_athena.conf" +struct AccountName : VString<23> {}; +struct AccountPass : VString<23> {}; +struct AccountCrypt : VString<39> {}; +struct AccountEmail : VString<39> {}; +struct ServerName : VString<19> {}; +struct PartyName : VString<23> {}; +struct VarName : VString<31> {}; +template +T stringish(VString iv) +{ + T rv; + static_cast&>(rv) = iv; + return rv; +} +#define DEFAULT_EMAIL stringish("a@a.com") + +// It is decreed: a mapname shall not contain an extension +class MapName : public strings::_crtp_string +{ + VString<15> _impl; +public: + MapName() = default; + MapName(VString<15> v) : _impl(v.oislice_h(std::find(v.begin(), v.end(), '.'))) {} + + iterator begin() const { return &*_impl.begin(); } + iterator end() const { return &*_impl.begin(); } + const char *c_str() const { return _impl.c_str(); } + + operator FString() const { return _impl; } + operator TString() const { return _impl; } + operator SString() const { return _impl; } + operator ZString() const { return _impl; } + operator XString() const { return _impl; } +}; +template<> +inline +MapName stringish(VString<15> iv) +{ + return iv; +} +inline +const char *decay_for_printf(const MapName& vs) { return vs.c_str(); } + +// It is decreed: a charname is sometimes case sensitive +struct CharName +{ +private: + VString<23> _impl; +public: + CharName() = default; + explicit CharName(VString<23> name) + : _impl(name) + {} + + VString<23> to__actual() const + { + return _impl; + } + VString<23> to__lower() const + { + return _impl.to_lower(); + } + VString<23> to__upper() const + { + return _impl.to_upper(); + } + VString<23> to__canonical() const + { +#if NAME_IGNORING_CASE == 0 + return to__actual(); +#endif +#if NAME_IGNORING_CASE == 1 + return to__lower(); +#endif + } + + friend bool operator == (const CharName& l, const CharName& r) + { return l.to__canonical() == r.to__canonical(); } + friend bool operator != (const CharName& l, const CharName& r) + { return l.to__canonical() != r.to__canonical(); } + friend bool operator < (const CharName& l, const CharName& r) + { return l.to__canonical() < r.to__canonical(); } + friend bool operator <= (const CharName& l, const CharName& r) + { return l.to__canonical() <= r.to__canonical(); } + friend bool operator > (const CharName& l, const CharName& r) + { return l.to__canonical() > r.to__canonical(); } + friend bool operator >= (const CharName& l, const CharName& r) + { return l.to__canonical() >= r.to__canonical(); } + + friend + VString<23> convert_for_printf(const CharName& vs) { return vs.to__actual(); } +}; +template<> +inline +CharName stringish(VString<23> iv) +{ + return CharName(iv); +} + namespace e { enum class EPOS : uint16_t @@ -78,7 +180,7 @@ struct item struct point { - char map_[16]; + MapName map_; short x, y; }; @@ -105,7 +207,7 @@ struct skill_value struct global_reg { - char str[32]; + VarName str; int value; }; @@ -182,7 +284,7 @@ struct mmo_charstatus short shield; short head_top, head_mid, head_bottom; - char name[24]; + CharName name; unsigned char base_level, job_level; earray attrs; unsigned char char_num, sex; @@ -221,7 +323,8 @@ struct GM_Account struct party_member { int account_id; - char name[24], map[24]; + CharName name; + MapName map; int leader, online, lv; struct map_session_data *sd; }; @@ -229,16 +332,10 @@ struct party_member struct party { int party_id; - char name[24]; + PartyName name; int exp; int item; struct party_member member[MAX_PARTY]; }; -struct square -{ - int val1[5]; - int val2[5]; -}; - #endif // MMO_HPP diff --git a/src/common/sanity.hpp b/src/common/sanity.hpp index 74e24df..3658f9f 100644 --- a/src/common/sanity.hpp +++ b/src/common/sanity.hpp @@ -22,29 +22,6 @@ # if __GNUC_MINOR__ < 6 && !defined(__clang__) # error "Please upgrade to at least GCC 4.6" # endif // __GNUC_MINOR__ < 6 && !defined(__clang__) -// temporary workaround for library issues -// since __GLIBCXX__ is hard to use -# if __GNUC_MINOR__ == 6 -# define WORKAROUND_GCC46_COMPILER -# endif // __GNUC_MINOR__ == 6 -# ifdef __GLIBCXX__ -// versions of libstdc++ from gcc -// 4.6.0, 4.6.1, 4.6.2, 4.6.3, 4.6.4 -# if __GLIBCXX__ == 20110325 \ - || __GLIBCXX__ == 20110627 \ - || __GLIBCXX__ == 20111026 \ - || __GLIBCXX__ == 20120301 \ - || __GLIBCXX__ == 20130412 \ - /* various Debian snapshots */ \ - || __GLIBCXX__ == 20121127 \ - || __GLIBCXX__ == 20130114 -# define WORKAROUND_GCC46_LIBRARY -# endif // __GLIBCXX__ == ... -# endif // defined __GLIBCXX__ -# if defined(WORKAROUND_GCC46_COMPILER) \ - && !defined(WORKAROUND_GCC46_LIBRARY) -# error "Unknown gcc 4.6.x release" -# endif // compiler and not library # endif // __GNUC__ == 4 # if not defined(__i386__) and not defined(__x86_64__) diff --git a/src/common/socket.cpp b/src/common/socket.cpp index 214fb5a..2d08171 100644 --- a/src/common/socket.cpp +++ b/src/common/socket.cpp @@ -21,8 +21,6 @@ static fd_set readfds; int fd_max; -static -int currentuse; static const uint32_t RFIFO_SIZE = 65536; @@ -128,16 +126,17 @@ void connect_client(int listen_fd) perror("accept"); return; } - if (fd_max <= fd) - { - fd_max = fd + 1; - } - if (!free_fds()) + if (fd >= SOFT_LIMIT) { FPRINTF(stderr, "softlimit reached, disconnecting : %d\n", fd); - delete_session(fd); + shutdown(fd, SHUT_RDWR); + close(fd); return; } + if (fd_max <= fd) + { + fd_max = fd + 1; + } const int yes = 1; /// Allow to bind() again after the server restarts. @@ -178,8 +177,6 @@ void connect_client(int listen_fd) session[fd]->client_addr = client_address; session[fd]->created = TimeT::now(); session[fd]->connected = 0; - - currentuse++; } int make_listen_port(uint16_t port) @@ -237,7 +234,6 @@ int make_listen_port(uint16_t port) session[fd]->created = TimeT::now(); session[fd]->connected = 1; - currentuse++; return fd; } @@ -295,7 +291,6 @@ int make_connection(uint32_t ip, uint16_t port) session[fd]->created = TimeT::now(); session[fd]->connected = 1; - currentuse++; return fd; } @@ -326,13 +321,6 @@ void delete_session(int fd) // just close() would try to keep sending buffers shutdown(fd, SHUT_RDWR); close(fd); - currentuse--; - if (currentuse < 0) - { - FPRINTF(stderr, "delete_session: current sessions negative!\n"); - currentuse = 0; - } - return; } void realloc_fifo(int fd, size_t rfifo_size, size_t wfifo_size) @@ -446,7 +434,6 @@ void do_socket(void) #pragma GCC diagnostic ignored "-Wold-style-cast" FD_ZERO(&readfds); #pragma GCC diagnostic pop - currentuse = 3; } void RFIFOSKIP(int fd, size_t len) @@ -460,23 +447,3 @@ void RFIFOSKIP(int fd, size_t len) abort(); } } - -void fclose_(FILE * fp) -{ - if (fclose(fp)) - perror("fclose"), abort(); - currentuse--; -} - -FILE *fopen_(const char *path, const char *mode) -{ - FILE *f = fopen(path, mode); - if (f) - currentuse++; - return f; -} - -bool free_fds(void) -{ - return currentuse < SOFT_LIMIT; -} diff --git a/src/common/socket.hpp b/src/common/socket.hpp index 2366373..dd1c872 100644 --- a/src/common/socket.hpp +++ b/src/common/socket.hpp @@ -96,12 +96,6 @@ void do_socket(void); // themselves as servers void set_defaultparse(void(*defaultparse)(int)); -/// Wrappers to track number of free FDs -void fclose_(FILE * fp); -FILE *fopen_(const char *path, const char *mode); - -bool free_fds(void); - template uint8_t *pod_addressof_m(T& structure) { @@ -149,10 +143,22 @@ void RFIFO_STRUCT(int fd, size_t pos, T& structure) { really_memcpy(pod_addressof_m(structure), static_cast(RFIFOP(fd, pos)), sizeof(T)); } +template +inline +VString RFIFO_STRING(int fd, size_t pos) +{ + const char *const begin = static_cast(RFIFOP(fd, pos)); + const char *const end = begin + len-1; + const char *const mid = std::find(begin, end, '\0'); + return XString(begin, mid, nullptr); +} inline -void RFIFO_STRING(int fd, size_t pos, char *out, size_t len) +FString RFIFO_STRING(int fd, size_t pos, size_t len) { - strzcpy(out, static_cast(RFIFOP(fd, pos)), len); + const char *const begin = static_cast(RFIFOP(fd, pos)); + const char *const end = begin + len; + const char *const mid = std::find(begin, end, '\0'); + return XString(begin, mid, nullptr); } inline void RFIFO_BUF_CLONE(int fd, uint8_t *buf, size_t len) @@ -189,14 +195,27 @@ void RBUF_STRUCT(const uint8_t *p, size_t pos, T& structure) { really_memcpy(pod_addressof_m(structure), p + pos, sizeof(T)); } +template +inline +VString RBUF_STRING(const uint8_t *p, size_t pos) +{ + const char *const begin = static_cast(RBUFP(p, pos)); + const char *const end = begin + len-1; + const char *const mid = std::find(begin, end, '\0'); + return XString(begin, mid, nullptr); +} inline -void RBUF_STRING(const uint8_t *p, size_t pos, char *out, size_t len) +FString RBUF_STRING(const uint8_t *p, size_t pos, size_t len) { - strzcpy(out, static_cast(RBUFP(p, pos)), len); + const char *const begin = static_cast(RBUFP(p, pos)); + const char *const end = begin + len; + const char *const mid = std::find(begin, end, '\0'); + return XString(begin, mid, nullptr); } /// Unused - check how much data can be written +// the existence of this seems scary inline size_t WFIFOSPACE(int fd) { @@ -229,9 +248,12 @@ void WFIFO_STRUCT(int fd, size_t pos, T& structure) really_memcpy(static_cast(WFIFOP(fd, pos)), pod_addressof_c(structure), sizeof(T)); } inline -void WFIFO_STRING(int fd, size_t pos, const char *s, size_t len) +void WFIFO_STRING(int fd, size_t pos, XString s, size_t len) { - strzcpy(static_cast(WFIFOP(fd, pos)), s, len); + char *const begin = static_cast(WFIFOP(fd, pos)); + char *const end = begin + len; + char *const mid = std::copy(s.begin(), s.end(), begin); + std::fill(mid, end, '\0'); } inline void WFIFO_ZERO(int fd, size_t pos, size_t len) @@ -276,9 +298,12 @@ void WBUF_STRUCT(uint8_t *p, size_t pos, T& structure) really_memcpy(p + pos, pod_addressof_c(structure), sizeof(T)); } inline -void WBUF_STRING(uint8_t *p, size_t pos, const char *s, size_t len) +void WBUF_STRING(uint8_t *p, size_t pos, XString s, size_t len) { - strzcpy(static_cast(WBUFP(p, pos)), s, len); + char *const begin = static_cast(WBUFP(p, pos)); + char *const end = begin + len; + char *const mid = std::copy(s.begin(), s.end(), begin); + std::fill(mid, end, '\0'); } inline void WBUF_ZERO(uint8_t *p, size_t pos, size_t len) diff --git a/src/common/strings.hpp b/src/common/strings.hpp index 8562ec4..ead3f52 100644 --- a/src/common/strings.hpp +++ b/src/common/strings.hpp @@ -22,10 +22,17 @@ #include "sanity.hpp" #include +#include #include +#include +#include #include -#include +#include +#include +#include + +#include "utils2.hpp" // It is a common mistake to assume that one string class for everything. // Because C++ and TMWA have a C legacy, there are a few more here @@ -64,13 +71,13 @@ namespace strings _iterator(const char *p=nullptr) : _ptr(p) {} // iterator - reference operator *() { return *_ptr; } + reference operator *() const { return *_ptr; } X& operator ++() { ++_ptr; return *this; } // equality comparable friend bool operator == (X l, X r) { return l._ptr == r._ptr; } // input iterator friend bool operator != (X l, X r) { return !(l == r); } - pointer operator->() { return _ptr; } + pointer operator->() const { return _ptr; } X operator++ (int) { X out = *this; ++*this; return out; } // forward iterator is mostly semantical, and the ctor is above // bidirectional iterator @@ -83,8 +90,8 @@ namespace strings X& operator -= (difference_type n) { _ptr -= n; return *this; } friend X operator - (X a, difference_type n) { return a -= n; } friend difference_type operator - (X b, X a) { return b._ptr - a._ptr; } - reference operator[](difference_type n) { return _ptr[n]; } - friend bool operator < (X a, X b) { return a._ptr - b._ptr; } + reference operator[](difference_type n) const { return _ptr[n]; } + friend bool operator < (X a, X b) { return a._ptr < b._ptr; } friend bool operator > (X a, X b) { return b < a; } friend bool operator >= (X a, X b) { return !(a < b); } friend bool operator <= (X a, X b) { return !(a > b); } @@ -92,7 +99,7 @@ namespace strings /// A helper class that implements all the interesting stuff that can /// be done on any constant string, in terms of .begin() and .end(). - template + template class _crtp_string { public: @@ -108,22 +115,67 @@ namespace strings size_t size() const { return end() - begin(); } reverse_iterator rbegin() const { return reverse_iterator(end()); } reverse_iterator rend() const { return reverse_iterator(begin()); } - operator bool() { return size(); } - bool operator !() { return !size(); } + explicit + operator bool() const { return size(); } + bool operator !() const { return !size(); } char operator[](size_t i) const { return begin()[i]; } char front() const { return *begin(); } char back() const { return end()[-1]; } const char *data() { return &*begin(); } - XString xslice_t(size_t o) const; - XString xslice_h(size_t o) const; - XString xrslice_t(size_t no) const; - XString xrslice_h(size_t no) const; - XString xlslice(size_t o, size_t l) const; - XString xpslice(size_t b, size_t e) const; + Z xslice_t(size_t o) const; + X xslice_h(size_t o) const; + Z xrslice_t(size_t no) const; + X xrslice_h(size_t no) const; + Z xislice_t(iterator it) const; + X xislice_h(iterator it) const; + X xlslice(size_t o, size_t l) const; + X xpslice(size_t b, size_t e) const; + X xislice(iterator b, iterator e) const; + Z lstrip() const; + X rstrip() const; + X strip() const; + bool startswith(XString x) const; bool endswith(XString x) const; + bool startswith(char c) const; + bool endswith(char c) const; + + bool contains(char c) const; + bool contains_seq(XString s) const; + bool contains_any(XString s) const; + + bool has_print() const; + bool is_print() const; + __attribute__((deprecated)) + O to_print() const; + + bool is_graph() const; + bool has_graph() const; + + bool has_lower() const; + bool is_lower() const; + O to_lower() const; + + bool has_upper() const; + bool is_upper() const; + O to_upper() const; + + bool has_alpha() const; // equivalent to has_lower || has_upper + bool is_alpha() const; // NOT equivalent to is_lower || is_upper + + bool has_digit2() const; + bool is_digit2() const; + bool has_digit8() const; + bool is_digit8() const; + bool has_digit10() const; + bool is_digit10() const; + bool has_digit16() const; + bool is_digit16() const; + + bool has_alnum() const; // equivalent to has_alpha || has_digit10 + bool is_alnum() const; // NOT equivalent to is_alpha || is_digit10 }; @@ -134,53 +186,112 @@ namespace strings class MString { public: - typedef char *iterator; - typedef _iterator const_iterator; + typedef std::deque::iterator iterator; + typedef std::deque::const_iterator const_iterator; typedef std::reverse_iterator reverse_iterator; typedef std::reverse_iterator const_reverse_iterator; private: - std::string _hack; + std::deque _hack; public: - template - MString(char (&s)[n]) = delete; - template - MString(const char (&s)[n]) : _hack(s) {} - template - MString(It b, It e) : _hack(b, e) {} - - iterator begin() { return &*_hack.begin(); } - iterator end() { return &*_hack.end(); } - const_iterator begin() const { return &*_hack.begin(); } - const_iterator end() const { return &*_hack.end(); } + iterator begin() { return _hack.begin(); } + iterator end() { return _hack.end(); } + const_iterator begin() const { return _hack.begin(); } + const_iterator end() const { return _hack.end(); } reverse_iterator rbegin() { return reverse_iterator(end()); } reverse_iterator rend() { return reverse_iterator(begin()); } const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } + + size_t size() const { return _hack.size(); } + explicit + operator bool() const { return size(); } + bool operator !() const { return !size(); } + + MString& operator += (MString rhs) + { + _hack.insert(_hack.end(), rhs.begin(), rhs.end()); + return *this; + } + MString& operator += (char c) + { + _hack.push_back(c); + return *this; + } + MString& operator += (XString xs); + + void pop_back(size_t n=1) + { + while (n--) + _hack.pop_back(); + } + char& front() + { + return _hack.front(); + } + char& back() + { + return _hack.back(); + } }; /// An owning string that has reached its final contents. /// The storage is NUL-terminated /// TODO implement a special one, that guarantees refcounting. - class FString : public _crtp_string + class FString : public _crtp_string { - /*const*/ std::string _hack; + std::shared_ptr> _hack2; + + template + void _assign(It b, It e) + { + if (b == e) + { + // TODO use a special empty object + // return; + } + if (!std::is_base_of::iterator_category>::value) + { + // can't use std::distance + _hack2 = std::make_shared>(); + for (; b != e; ++b) + _hack2->push_back(*b); + _hack2->push_back('\0'); + _hack2->shrink_to_fit(); + } + size_t diff = std::distance(b, e); + _hack2 = std::make_shared>(diff + 1, '\0'); + std::copy(b, e, _hack2->begin()); + } public: -#ifndef __clang__ - __attribute__((warning("This should be removed in the next diff"))) -#endif - FString(std::string s) : _hack(std::move(s)) {} + FString() + { + const char *sadness = ""; + _assign(sadness, sadness); + } + + explicit FString(const MString& s) + { + _assign(s.begin(), s.end()); + } - FString() : _hack() {} - FString(const MString& s) : _hack(s.begin(), s.end()) {} template FString(char (&s)[n]) = delete; + template - FString(const char (&s)[n]) : _hack(s) {} + FString(const char (&s)[n]) + { + _assign(s, s + strlen(s)); + } + template - FString(It b, It e) : _hack(b, e) {} + FString(It b, It e) + { + _assign(b, e); + } + - iterator begin() const { return &*_hack.begin(); } - iterator end() const { return &*_hack.end(); } + iterator begin() const { return &_hack2->begin()[0]; } + iterator end() const { return &_hack2->end()[-1]; } const FString *base() const { return this; } const char *c_str() const { return &*begin(); } @@ -188,13 +299,16 @@ namespace strings SString oslice_h(size_t o) const; TString orslice_t(size_t no) const; SString orslice_h(size_t no) const; + TString oislice_t(iterator it) const; + SString oislice_h(iterator it) const; SString olslice(size_t o, size_t l) const; SString opslice(size_t b, size_t e) const; + SString oislice(iterator b, iterator e) const; }; /// An owning string that represents a tail slice of an FString. /// Guaranteed to be NUL-terminated. - class TString : public _crtp_string + class TString : public _crtp_string { friend class SString; FString _s; @@ -206,6 +320,8 @@ namespace strings TString(char (&s)[n]) = delete; template TString(const char (&s)[n]) : _s(s), _o(0) {} + //template + //TString(It b, It e) : _s(b, e), _o(0) {} iterator begin() const { return &_s.begin()[_o]; } iterator end() const { return &*_s.end(); } @@ -216,28 +332,32 @@ namespace strings SString oslice_h(size_t o) const; TString orslice_t(size_t no) const; SString orslice_h(size_t no) const; + TString oislice_t(iterator it) const; + SString oislice_h(iterator it) const; SString olslice(size_t o, size_t l) const; SString opslice(size_t b, size_t e) const; + SString oislice(iterator b, iterator e) const; - operator FString() + operator FString() const { if (_o) return FString(begin(), end()); else return _s; } }; /// An owning string that represents a arbitrary slice of an FString. /// Not guaranteed to be NUL-terminated. - class SString : public _crtp_string + class SString : public _crtp_string { FString _s; size_t _b, _e; public: SString() : _s(), _b(), _e() {} SString(FString f) : _s(std::move(f)), _b(), _e(_s.size()) {} - SString(TString t) : _s(t._s), _e(_s.size()) {} + SString(TString t) : _s(t._s), _b(0), _e(_s.size()) {} template SString(char (&s)[n]) = delete; template SString(const char (&s)[n]) : _s(s), _b(0), _e(_s.size()) {} - + //template + //SString(It b, It e) : _s(b, e), _b(0), _e(_s.size()) {} SString(FString f, size_t b, size_t e) : _s(std::move(f)), _b(b), _e(e) {} iterator begin() const { return &_s.begin()[_b]; } @@ -248,34 +368,34 @@ namespace strings SString oslice_h(size_t o) const; SString orslice_t(size_t no) const; SString orslice_h(size_t no) const; + SString oislice_t(iterator it) const; + SString oislice_h(iterator it) const; SString olslice(size_t o, size_t l) const; SString opslice(size_t b, size_t e) const; + SString oislice(iterator b, iterator e) const; - operator FString() + operator FString() const { if (_b == 0 && _e == _s.size()) return _s; else return FString(begin(), end()); } - operator TString() + operator TString() const { if (_e == _s.size()) return TString(_s, _b); else return FString(begin(), end()); } }; /// A non-owning string that is guaranteed to be NUL-terminated. /// This should be only used as a parameter. - class ZString : public _crtp_string + class ZString : public _crtp_string { iterator _b, _e; // optional const FString *_base; public: -#ifndef __clang__ - __attribute__((warning("This should be removed in the next diff"))) -#endif - ZString(const std::string& s) : _b(&*s.begin()), _e(&*s.end()), _base(nullptr) {} - enum { really_construct_from_a_pointer }; ZString() { *this = ZString(""); } // no MString ZString(const FString& s) : _b(&*s.begin()), _e(&*s.end()), _base(s.base()) {} ZString(const TString& s) : _b(&*s.begin()), _e(&*s.end()), _base(s.base()) {} ZString(const SString&) = delete; + // dangerous + ZString(const char *b, const char *e, const FString *base_) : _b(b), _e(e), _base(base_) {} ZString(decltype(really_construct_from_a_pointer), const char *s, const FString *base_) : _b(s), _e(s + strlen(s)), _base(base_) {} template ZString(char (&s)[n]) = delete; @@ -291,27 +411,30 @@ namespace strings XString oslice_h(size_t o) const; ZString orslice_t(size_t no) const; XString orslice_h(size_t no) const; + ZString oislice_t(iterator it) const; + XString oislice_h(iterator it) const; XString olslice(size_t o, size_t l) const; XString opslice(size_t b, size_t e) const; + XString oislice(iterator b, iterator e) const; - operator FString() + operator FString() const { if (_base) return SString(*_base, &*_b - &*_base->begin(), &*_e - &*_base->begin()); else return FString(_b, _e); } - operator TString() + operator TString() const { if (_base) return SString(*_base, &*_b - &*_base->begin(), &*_e - &*_base->begin()); else return FString(_b, _e); } - operator SString() + operator SString() const { if (_base) return SString(*_base, &*_b - &*_base->begin(), &*_e - &*_base->begin()); else return FString(_b, _e); } }; /// A non-owning string that is not guaranteed to be NUL-terminated. /// This should be only used as a parameter. - class XString : public _crtp_string + class XString : public _crtp_string { iterator _b, _e; // optional const FString *_base; public: // do I really want this? - XString() : _b(nullptr), _e(nullptr) {} + XString() : _b(""), _e(_b), _base() {} XString(std::nullptr_t) = delete; // no MString XString(const FString& s) : _b(&*s.begin()), _e(&*s.end()), _base(s.base()) {} @@ -331,32 +454,34 @@ namespace strings iterator begin() const { return _b; } iterator end() const { return _e; } - const FString *base() const { return _base; }; + const FString *base() const { return _base; } XString oslice_t(size_t o) const { return xslice_t(o); } XString oslice_h(size_t o) const { return xslice_h(o); } XString orslice_t(size_t no) const { return xrslice_t(no); } XString orslice_h(size_t no) const { return xrslice_h(no); } + XString oislice_t(iterator it) const { return xislice_t(it); } + XString oislice_h(iterator it) const { return xislice_h(it); } XString olslice(size_t o, size_t l) const { return xlslice(o, l); } XString opslice(size_t b, size_t e) const { return xpslice(b, e); } + XString oislice(iterator b, iterator e) const { return xislice(b, e); } - operator FString() + operator FString() const { if (_base) return SString(*_base, &*_b - &*_base->begin(), &*_e - &*_base->begin()); else return FString(_b, _e); } - operator TString() + operator TString() const { if (_base) return SString(*_base, &*_b - &*_base->begin(), &*_e - &*_base->begin()); else return FString(_b, _e); } - operator SString() + operator SString() const { if (_base) return SString(*_base, &*_b - &*_base->begin(), &*_e - &*_base->begin()); else return FString(_b, _e); } - operator ZString() = delete; + operator ZString() const = delete; }; template - class VString : public _crtp_string> + class VString : public _crtp_string, VString, ZString, XString> { char _data[n]; unsigned char _special; - typedef typename _crtp_string>::iterator iterator; public: - static_assert(n & 1, "Size should probably be odd."); + typedef typename _crtp_string, VString, ZString, XString>::iterator iterator; VString(XString x) : _data(), _special() { if (x.size() > n) @@ -397,71 +522,181 @@ namespace strings { *this = XString(e, s, nullptr); } + VString(char c) + { + *this = XString(&c, &c + 1, nullptr); + } VString() { *this = XString(); } + // hopefully this is obvious iterator begin() const { return std::begin(_data); } iterator end() const { return std::end(_data) - _special; } - const FString *base() const { return nullptr; }; + const FString *base() const { return nullptr; } const char *c_str() const { return &*begin(); } VString oslice_t(size_t o) const { return this->xslice_t(o); } VString oslice_h(size_t o) const { return this->xslice_h(o); } VString orslice_t(size_t no) const { return this->xrslice_t(no); } VString orslice_h(size_t no) const { return this->xrslice_h(no); } + VString oislice_t(iterator it) const { return this->xislice_t(it); } + VString oislice_h(iterator it) const { return this->xislice_h(it); } VString olslice(size_t o, size_t l) const { return this->xlslice(o, l); } VString opslice(size_t b, size_t e) const { return this->xpslice(b, e); } + VString oislice(iterator b, iterator e) const { return this->xislice(b, e); } operator FString() const { return FString(begin(), end()); } operator TString() const { return FString(begin(), end()); } operator SString() const { return FString(begin(), end()); } operator ZString() const { return ZString(_data); } operator XString() const { return XString(&*begin(), &*end(), nullptr); } - }; + template + operator VString() const + { + static_assert(m > n, "can only grow"); + XString x = *this; + return VString(XString(x)); + } + }; // not really intended for public use inline int xstr_compare(XString l, XString r) { - return std::lexicographical_compare( + bool less = std::lexicographical_compare( l.begin(), l.end(), r.begin(), r.end()); + bool greater = std::lexicographical_compare( + r.begin(), r.end(), + l.begin(), l.end()); + return greater - less; } + template - bool operator == (const L& l, const R& r) + class string_comparison_allowed + { + constexpr static bool l_is_vstring_exact = std::is_same, L>::value; + constexpr static bool l_is_vstring_approx = std::is_base_of, L>::value; + constexpr static bool r_is_vstring_exact = std::is_same, R>::value; + constexpr static bool r_is_vstring_approx = std::is_base_of, R>::value; + + constexpr static bool l_is_restricted = l_is_vstring_approx && !l_is_vstring_exact; + constexpr static bool r_is_restricted = r_is_vstring_approx && !r_is_vstring_exact; + public: + constexpr static bool value = std::is_same::value || (!l_is_restricted && !r_is_restricted); + }; + + struct _test : VString<1> {}; + struct _test2 : VString<1> {}; + + static_assert(string_comparison_allowed<_test, _test>::value, "tt"); + static_assert(string_comparison_allowed, VString<1>>::value, "vv"); + static_assert(!string_comparison_allowed<_test, XString>::value, "tx"); + static_assert(!string_comparison_allowed<_test, VString<1>>::value, "tv"); + static_assert(!string_comparison_allowed<_test, _test2>::value, "t2"); + static_assert(string_comparison_allowed, XString>::value, "vx"); + static_assert(string_comparison_allowed::value, "xx"); + static_assert(string_comparison_allowed::value, "xf"); + + template::value>::type> + auto operator == (const L& l, const R& r) -> decltype((xstr_compare(l, r), true)) { return xstr_compare(l, r) == 0; } - template - bool operator != (const L& l, const R& r) + template::value>::type> + auto operator != (const L& l, const R& r) -> decltype((xstr_compare(l, r), true)) { return xstr_compare(l, r) != 0; } - template - bool operator < (const L& l, const R& r) + template::value>::type> + auto operator < (const L& l, const R& r) -> decltype((xstr_compare(l, r), true)) { return xstr_compare(l, r) < 0; } - template - bool operator <= (const L& l, const R& r) + template::value>::type> + auto operator <= (const L& l, const R& r) -> decltype((xstr_compare(l, r), true)) { return xstr_compare(l, r) <= 0; } - template - bool operator > (const L& l, const R& r) + template::value>::type> + auto operator > (const L& l, const R& r) -> decltype((xstr_compare(l, r), true)) { return xstr_compare(l, r) > 0; } - template - bool operator >= (const L& l, const R& r) + template::value>::type> + auto operator >= (const L& l, const R& r) -> decltype((xstr_compare(l, r), true)) { return xstr_compare(l, r) >= 0; } + namespace detail + { + constexpr + bool is_print(char c) + { + return ' ' <= c && c <= '~'; + } + constexpr + bool is_graph(char c) + { + return is_print(c) && c != ' '; + } + constexpr + bool is_lower(char c) + { + return 'a' <= c && c <= 'z'; + } + constexpr + bool is_upper(char c) + { + return 'A' <= c && c <= 'Z'; + } + constexpr + bool is_alpha(char c) + { + return is_lower(c) || is_upper(c); + } + constexpr + bool is_digit2(char c) + { + return '0' <= c && c <= '1'; + } + constexpr + bool is_digit8(char c) + { + return '0' <= c && c <= '7'; + } + constexpr + bool is_digit10(char c) + { + return '0' <= c && c <= '9'; + } + constexpr + bool is_digit16(char c) + { + return ('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f'); + } + constexpr + bool is_alnum(char c) + { + return is_alpha(c) || is_digit10(c); + } + + constexpr + char to_lower(char c) + { + return is_upper(c) ? c | ' ' : c; + } + constexpr + char to_upper(char c) + { + return is_lower(c) ? c & ~' ' : c; + } + } // sadness typedef MString MS; @@ -472,30 +707,176 @@ namespace strings typedef XString XS; // _crtp_string - template - XS _crtp_string::xslice_t(size_t o) const - { return XS(&begin()[o], &*end(), base()); } - template - XS _crtp_string::xslice_h(size_t o) const - { return XS(&*begin(), &begin()[o], base()); } - template - XS _crtp_string::xrslice_t(size_t no) const - { return XS(&end()[-no], &*end(), base()); } - template - XS _crtp_string::xrslice_h(size_t no) const - { return XS(&*begin(), &end()[-no], base()); } - template - XS _crtp_string::xlslice(size_t o, size_t l) const - { return XS(&begin()[o], &begin()[o + l], base()); } - template - XS _crtp_string::xpslice(size_t b, size_t e) const - { return XS(&begin()[b], &begin()[e], base()); } - template - bool _crtp_string::startswith(XS x) const - { return size() > x.size() && xslice_h(x.size()) == x; } - template - bool _crtp_string::endswith(XS x) const + template + Z _crtp_string::xslice_t(size_t o) const + { return Z(&begin()[o], &*end(), base()); } + template + X _crtp_string::xslice_h(size_t o) const + { return X(&*begin(), &begin()[o], base()); } + template + Z _crtp_string::xrslice_t(size_t no) const + { return Z(&end()[-no], &*end(), base()); } + template + X _crtp_string::xrslice_h(size_t no) const + { return X(&*begin(), &end()[-no], base()); } + template + Z _crtp_string::xislice_t(iterator it) const + { return Z(&*it, &*end(), base()); } + template + X _crtp_string::xislice_h(iterator it) const + { return X(&*begin(), &*it, base()); } + template + X _crtp_string::xlslice(size_t o, size_t l) const + { return X(&begin()[o], &begin()[o + l], base()); } + template + X _crtp_string::xpslice(size_t b, size_t e) const + { return X(&begin()[b], &begin()[e], base()); } + template + X _crtp_string::xislice(iterator b, iterator e) const + { return X(&*b, &*e, base()); } + template + Z _crtp_string::lstrip() const + { + Z z = _ref(); + while (z.startswith(' ')) + z = z.xslice_t(1); + return z; + } + template + X _crtp_string::rstrip() const + { + X x = _ref(); + while (x.endswith(' ')) + x = x.xrslice_h(1); + return x; + } + template + X _crtp_string::strip() const + { return lstrip().rstrip(); } + + template + bool _crtp_string::startswith(XS x) const + { return size() >= x.size() && xslice_h(x.size()) == x; } + template + bool _crtp_string::endswith(XS x) const { return size() > x.size() && xrslice_t(x.size()) == x; } + template + bool _crtp_string::startswith(char c) const + { return size() && front() == c; } + template + bool _crtp_string::endswith(char c) const + { return size() && back() == c; } + template + bool _crtp_string::contains(char c) const + { return std::find(begin(), end(), c) != end(); } + template + bool _crtp_string::contains_seq(XString s) const + { return std::search(begin(), end(), s.begin(), s.end()) != end(); } + template + bool _crtp_string::contains_any(XString s) const + { return std::find_if(begin(), end(), [s](char c) { return s.contains(c); }) != end(); } + + template + bool _crtp_string::has_print() const + { return std::find_if(begin(), end(), detail::is_print) != end(); } + template + bool _crtp_string::is_print() const + { return std::find_if_not(begin(), end(), detail::is_print) == end(); } + template + O _crtp_string::to_print() const + { + if (is_print()) return _ref(); + char buf[size()]; + char *const b = buf; + char *const e = std::transform(begin(), end(), b, [](char c) { return detail::is_print(c) ? c : '_'; }); + return XString(b, e, nullptr); + } + + template + bool _crtp_string::has_graph() const + { return std::find_if(begin(), end(), detail::is_graph) != end(); } + template + bool _crtp_string::is_graph() const + { return std::find_if_not(begin(), end(), detail::is_graph) == end(); } + + template + bool _crtp_string::has_lower() const + { return std::find_if(begin(), end(), detail::is_lower) != end(); } + template + bool _crtp_string::is_lower() const + { return std::find_if_not(begin(), end(), detail::is_lower) == end(); } + template + O _crtp_string::to_lower() const + { + if (!has_upper()) return _ref(); + char buf[size()]; + char *const b = buf; + char *const e = std::transform(begin(), end(), b, detail::to_lower); + return XString(b, e, nullptr); + } + + template + bool _crtp_string::has_upper() const + { return std::find_if(begin(), end(), detail::is_upper) != end(); } + template + bool _crtp_string::is_upper() const + { return std::find_if_not(begin(), end(), detail::is_upper) == end(); } + template + O _crtp_string::to_upper() const + { + if (!has_lower()) return _ref(); + char buf[size()]; + char *const b = buf; + char *const e = std::transform(begin(), end(), b, detail::to_upper); + return XString(b, e, nullptr); + } + + template + bool _crtp_string::has_alpha() const + { return std::find_if(begin(), end(), detail::is_alpha) != end(); } + template + bool _crtp_string::is_alpha() const + { return std::find_if_not(begin(), end(), detail::is_alpha) == end(); } + + template + bool _crtp_string::has_digit2() const + { return std::find_if(begin(), end(), detail::is_digit2) != end(); } + template + bool _crtp_string::is_digit2() const + { return std::find_if_not(begin(), end(), detail::is_digit2) == end(); } + template + bool _crtp_string::has_digit8() const + { return std::find_if(begin(), end(), detail::is_digit8) != end(); } + template + bool _crtp_string::is_digit8() const + { return std::find_if_not(begin(), end(), detail::is_digit8) == end(); } + template + bool _crtp_string::has_digit10() const + { return std::find_if(begin(), end(), detail::is_digit10) != end(); } + template + bool _crtp_string::is_digit10() const + { return std::find_if_not(begin(), end(), detail::is_digit10) == end(); } + template + bool _crtp_string::has_digit16() const + { return std::find_if(begin(), end(), detail::is_digit16) != end(); } + template + bool _crtp_string::is_digit16() const + { return std::find_if_not(begin(), end(), detail::is_digit16) == end(); } + + template + bool _crtp_string::has_alnum() const + { return std::find_if(begin(), end(), detail::is_alnum) != end(); } + template + bool _crtp_string::is_alnum() const + { return std::find_if_not(begin(), end(), detail::is_alnum) == end(); } + + // MString + inline + MS& MS::operator += (XS x) + { + _hack.insert(_hack.end(), x.begin(), x.end()); + return *this; + } // FString inline @@ -511,11 +892,20 @@ namespace strings SS FS::orslice_h(size_t no) const { return SS(*this, 0, size() - no); } inline + TS FS::oislice_t(iterator it) const + { return TS(*this, it - begin()); } + inline + SS FS::oislice_h(iterator it) const + { return SS(*this, 0, it - begin()); } + inline SS FS::olslice(size_t o, size_t l) const { return SS(*this, o, o + l); } inline SS FS::opslice(size_t b, size_t e) const { return SS(*this, b, e); } + inline + SS FS::oislice(iterator b, iterator e) const + { return SS(*this, b - begin(), e - begin()); } // TString inline @@ -531,11 +921,20 @@ namespace strings SS TS::orslice_h(size_t no) const { return SS(_s, _o, _s.size() - no); } inline + TS TS::oislice_t(iterator it) const + { return TS(_s, _o + it - begin()); } + inline + SS TS::oislice_h(iterator it) const + { return SS(_s, _o, _o + it - begin()); } + inline SS TS::olslice(size_t o, size_t l) const { return SS(_s, _o + o, _o + o + l); } inline SS TS::opslice(size_t b, size_t e) const { return SS(_s, _o + b, _o + e); } + inline + SS TS::oislice(iterator b, iterator e) const + { return SS(_s, _o + b - begin(), _o + e - begin()); } // SString inline @@ -551,11 +950,20 @@ namespace strings SS SS::orslice_h(size_t no) const { return SS(_s, _b, _e - no); } inline + SS SS::oislice_t(iterator it) const + { return SS(_s, _b + it - begin(), _e); } + inline + SS SS::oislice_h(iterator it) const + { return SS(_s, _b, _b + it - begin()); } + inline SS SS::olslice(size_t o, size_t l) const { return SS(_s, _b + o, _b + o + l); } inline SS SS::opslice(size_t b, size_t e) const { return SS(_s, _b + b, _b + e); } + inline + SS SS::oislice(iterator b, iterator e) const + { return SS(_s, _b + b - begin(), _b + e - begin()); } // ZString inline @@ -571,22 +979,97 @@ namespace strings XS ZS::orslice_h(size_t no) const { return XS(&*begin(), &end()[-no], base()); } inline + ZS ZS::oislice_t(iterator it) const + { return ZS(really_construct_from_a_pointer, &*it, base()); } + inline + XS ZS::oislice_h(iterator it) const + { return XS(&*begin(), &*it, base()); } + inline XS ZS::olslice(size_t o, size_t l) const { return XS(&begin()[o], &begin()[o + l], base()); } inline XS ZS::opslice(size_t b, size_t e) const { return XS(&begin()[b], &begin()[e], base()); } + inline + XS ZS::oislice(iterator b, iterator e) const + { return XS(&*b, &*e, base()); } // cxxstdio helpers // I think the conversion will happen automatically. TODO test this. // Nope, it doesn't, since there's a template + // Actually, it might now. + inline + const char *decay_for_printf(const FString& fs) { return fs.c_str(); } inline - const char *convert_for_printf(const FString& fs) { return fs.c_str(); } + const char *decay_for_printf(const TString& ts) { return ts.c_str(); } inline - const char *convert_for_printf(const TString& ts) { return ts.c_str(); } + const char *decay_for_printf(const ZString& zs) { return zs.c_str(); } + template inline - const char *convert_for_printf(const ZString& zs) { return zs.c_str(); } + const char *decay_for_printf(const VString& vs) { return vs.c_str(); } + + template + inline __attribute__((format(printf, 2, 0))) + int do_vprint(VString& out, const char *fmt, va_list ap) + { + char buffer[len + 1]; + vsnprintf(buffer, len + 1, fmt, ap); + + out = const_(buffer); + return len; + } + + inline __attribute__((format(printf, 2, 0))) + int do_vprint(FString& out, const char *fmt, va_list ap) + { + int len; + { + va_list ap2; + va_copy(ap2, ap); + len = vsnprintf(nullptr, 0, fmt, ap2); + va_end(ap2); + } + char buffer[len + 1]; + vsnprintf(buffer, len + 1, fmt, ap); + + out = FString(buffer, buffer + len); + return len; + } + + inline __attribute__((format(scanf, 2, 0))) + int do_vscan(ZString in, const char *fmt, va_list ap) + { + return vsscanf(in.c_str(), fmt, ap); + } + + class StringConverter + { + FString& out; + char *mid; + public: + StringConverter(FString& s) + : out(s), mid(nullptr) + {} + ~StringConverter() + { + if (mid) + { + out = ZString(ZString::really_construct_from_a_pointer, mid, nullptr); + free(mid); + } + } + char **operator &() + { + return ∣ + } + }; + + inline + StringConverter convert_for_scanf(FString& s) + { + return StringConverter(s); + } } // namespace strings // owning diff --git a/src/common/strings2_test.cpp b/src/common/strings2_test.cpp new file mode 100644 index 0000000..fa4dc6f --- /dev/null +++ b/src/common/strings2_test.cpp @@ -0,0 +1,118 @@ +#include "strings.hpp" + +#include + +TEST(StringTests, traits2) +{ + ZString print_non = "\t\e"; + ZString print_mix = "n\t"; + FString print_all = "n "; + EXPECT_FALSE(print_non.has_print()); + EXPECT_TRUE(print_mix.has_print()); + EXPECT_TRUE(print_all.has_print()); + EXPECT_FALSE(print_non.is_print()); + EXPECT_FALSE(print_mix.is_print()); + EXPECT_TRUE(print_all.is_print()); + EXPECT_EQ("__", print_non.to_print()); + EXPECT_EQ("n_", print_mix.to_print()); + EXPECT_EQ("n ", print_all.to_print()); + EXPECT_EQ(print_all.begin(), print_all.to_print().begin()); + + ZString graph_non = " \e"; + ZString graph_mix = "n "; + FString graph_all = "n."; + EXPECT_FALSE(graph_non.has_graph()); + EXPECT_TRUE(graph_mix.has_graph()); + EXPECT_TRUE(graph_all.has_graph()); + EXPECT_FALSE(graph_non.is_graph()); + EXPECT_FALSE(graph_mix.is_graph()); + EXPECT_TRUE(graph_all.is_graph()); + + ZString lower_non = "0A"; + ZString lower_mix = "Oa"; + FString lower_all = "oa"; + EXPECT_FALSE(lower_non.has_lower()); + EXPECT_TRUE(lower_mix.has_lower()); + EXPECT_TRUE(lower_all.has_lower()); + EXPECT_FALSE(lower_non.is_lower()); + EXPECT_FALSE(lower_mix.is_lower()); + EXPECT_TRUE(lower_all.is_lower()); + EXPECT_EQ("0a", lower_non.to_lower()); + EXPECT_EQ("oa", lower_mix.to_lower()); + EXPECT_EQ("oa", lower_all.to_lower()); + EXPECT_EQ(lower_all.begin(), lower_all.to_lower().begin()); + + ZString upper_non = "0a"; + ZString upper_mix = "oA"; + FString upper_all = "OA"; + EXPECT_FALSE(upper_non.has_upper()); + EXPECT_TRUE(upper_mix.has_upper()); + EXPECT_TRUE(upper_all.has_upper()); + EXPECT_FALSE(upper_non.is_upper()); + EXPECT_FALSE(upper_mix.is_upper()); + EXPECT_TRUE(upper_all.is_upper()); + EXPECT_EQ("0A", upper_non.to_upper()); + EXPECT_EQ("OA", upper_mix.to_upper()); + EXPECT_EQ("OA", upper_all.to_upper()); + EXPECT_EQ(upper_all.begin(), upper_all.to_upper().begin()); + + ZString alpha_non = " 0"; + ZString alpha_mix = "n "; + FString alpha_all = "nA"; + EXPECT_FALSE(alpha_non.has_alpha()); + EXPECT_TRUE(alpha_mix.has_alpha()); + EXPECT_TRUE(alpha_all.has_alpha()); + EXPECT_FALSE(alpha_non.is_alpha()); + EXPECT_FALSE(alpha_mix.is_alpha()); + EXPECT_TRUE(alpha_all.is_alpha()); + + ZString digit2_non = "a9"; + ZString digit2_mix = "20"; + FString digit2_all = "01"; + EXPECT_FALSE(digit2_non.has_digit2()); + EXPECT_TRUE(digit2_mix.has_digit2()); + EXPECT_TRUE(digit2_all.has_digit2()); + EXPECT_FALSE(digit2_non.is_digit2()); + EXPECT_FALSE(digit2_mix.is_digit2()); + EXPECT_TRUE(digit2_all.is_digit2()); + + ZString digit8_non = "a9"; + ZString digit8_mix = "80"; + FString digit8_all = "37"; + EXPECT_FALSE(digit8_non.has_digit8()); + EXPECT_TRUE(digit8_mix.has_digit8()); + EXPECT_TRUE(digit8_all.has_digit8()); + EXPECT_FALSE(digit8_non.is_digit8()); + EXPECT_FALSE(digit8_mix.is_digit8()); + EXPECT_TRUE(digit8_all.is_digit8()); + + ZString digit10_non = "az"; + ZString digit10_mix = "a9"; + FString digit10_all = "42"; + EXPECT_FALSE(digit10_non.has_digit10()); + EXPECT_TRUE(digit10_mix.has_digit10()); + EXPECT_TRUE(digit10_all.has_digit10()); + EXPECT_FALSE(digit10_non.is_digit10()); + EXPECT_FALSE(digit10_mix.is_digit10()); + EXPECT_TRUE(digit10_all.is_digit10()); + + ZString digit16_non = "gz"; + ZString digit16_mix = "ao"; + FString digit16_all = "be"; + EXPECT_FALSE(digit16_non.has_digit16()); + EXPECT_TRUE(digit16_mix.has_digit16()); + EXPECT_TRUE(digit16_all.has_digit16()); + EXPECT_FALSE(digit16_non.is_digit16()); + EXPECT_FALSE(digit16_mix.is_digit16()); + EXPECT_TRUE(digit16_all.is_digit16()); + + ZString alnum_non = " ."; + ZString alnum_mix = "n "; + FString alnum_all = "n0"; + EXPECT_FALSE(alnum_non.has_alnum()); + EXPECT_TRUE(alnum_mix.has_alnum()); + EXPECT_TRUE(alnum_all.has_alnum()); + EXPECT_FALSE(alnum_non.is_alnum()); + EXPECT_FALSE(alnum_mix.is_alnum()); + EXPECT_TRUE(alnum_all.is_alnum()); +} diff --git a/src/common/strings_test.cpp b/src/common/strings_test.cpp index b6a6f67..fa04f1c 100644 --- a/src/common/strings_test.cpp +++ b/src/common/strings_test.cpp @@ -1,4 +1,6 @@ -#include "../../src/common/strings.hpp" +#include "strings.hpp" + +#include #include @@ -22,6 +24,118 @@ TYPED_TEST_P(StringTest, basic) const FString *base = hi.base(); } +TYPED_TEST_P(StringTest, order) +{ + TypeParam a; + TypeParam b("Hello"); + TypeParam c("Hello,"); + TypeParam d("World!"); + + // not using EXPECT_LT, etc. for better visibility + + EXPECT_FALSE(a < a); + EXPECT_TRUE(a < b); + EXPECT_TRUE(a < c); + EXPECT_TRUE(a < d); + EXPECT_FALSE(b < a); + EXPECT_FALSE(b < b); + EXPECT_TRUE(b < c); + EXPECT_TRUE(b < d); + EXPECT_FALSE(c < a); + EXPECT_FALSE(c < b); + EXPECT_FALSE(c < c); + EXPECT_TRUE(c < d); + EXPECT_FALSE(d < a); + EXPECT_FALSE(d < b); + EXPECT_FALSE(d < c); + EXPECT_FALSE(d < d); + + EXPECT_TRUE(a <= a); + EXPECT_TRUE(a <= b); + EXPECT_TRUE(a <= c); + EXPECT_TRUE(a <= d); + EXPECT_FALSE(b <= a); + EXPECT_TRUE(b <= b); + EXPECT_TRUE(b <= c); + EXPECT_TRUE(b <= d); + EXPECT_FALSE(c <= a); + EXPECT_FALSE(c <= b); + EXPECT_TRUE(c <= c); + EXPECT_TRUE(c <= d); + EXPECT_FALSE(d <= a); + EXPECT_FALSE(d <= b); + EXPECT_FALSE(d <= c); + EXPECT_TRUE(d <= d); + + EXPECT_TRUE(a >= a); + EXPECT_FALSE(a >= b); + EXPECT_FALSE(a >= c); + EXPECT_FALSE(a >= d); + EXPECT_TRUE(b >= a); + EXPECT_TRUE(b >= b); + EXPECT_FALSE(b >= c); + EXPECT_FALSE(b >= d); + EXPECT_TRUE(c >= a); + EXPECT_TRUE(c >= b); + EXPECT_TRUE(c >= c); + EXPECT_FALSE(c >= d); + EXPECT_TRUE(d >= a); + EXPECT_TRUE(d >= b); + EXPECT_TRUE(d >= c); + EXPECT_TRUE(d >= d); + + EXPECT_FALSE(a > a); + EXPECT_FALSE(a > b); + EXPECT_FALSE(a > c); + EXPECT_FALSE(a > d); + EXPECT_TRUE(b > a); + EXPECT_FALSE(b > b); + EXPECT_FALSE(b > c); + EXPECT_FALSE(b > d); + EXPECT_TRUE(c > a); + EXPECT_TRUE(c > b); + EXPECT_FALSE(c > c); + EXPECT_FALSE(c > d); + EXPECT_TRUE(d > a); + EXPECT_TRUE(d > b); + EXPECT_TRUE(d > c); + EXPECT_FALSE(d > d); + + EXPECT_TRUE(a == a); + EXPECT_FALSE(a == b); + EXPECT_FALSE(a == c); + EXPECT_FALSE(a == d); + EXPECT_FALSE(b == a); + EXPECT_TRUE(b == b); + EXPECT_FALSE(b == c); + EXPECT_FALSE(b == d); + EXPECT_FALSE(c == a); + EXPECT_FALSE(c == b); + EXPECT_TRUE(c == c); + EXPECT_FALSE(c == d); + EXPECT_FALSE(d == a); + EXPECT_FALSE(d == b); + EXPECT_FALSE(d == c); + EXPECT_TRUE(d == d); + + EXPECT_FALSE(a != a); + EXPECT_TRUE(a != b); + EXPECT_TRUE(a != c); + EXPECT_TRUE(a != d); + EXPECT_TRUE(b != a); + EXPECT_FALSE(b != b); + EXPECT_TRUE(b != c); + EXPECT_TRUE(b != d); + EXPECT_TRUE(c != a); + EXPECT_TRUE(c != b); + EXPECT_FALSE(c != c); + EXPECT_TRUE(c != d); + EXPECT_TRUE(d != a); + EXPECT_TRUE(d != b); + EXPECT_TRUE(d != c); + EXPECT_FALSE(d != d); +} + TYPED_TEST_P(StringTest, iterators) { TypeParam hi("Hello"); @@ -39,8 +153,12 @@ TYPED_TEST_P(StringTest, xslice) EXPECT_EQ("Hello,", hi.xslice_h(6)); EXPECT_EQ("World!", hi.xrslice_t(6)); EXPECT_EQ("Hello, ", hi.xrslice_h(6)); + typename TypeParam::iterator it = std::find(hi.begin(), hi.end(), ' '); + EXPECT_EQ(" World!", hi.xislice_t(it)); + EXPECT_EQ("Hello,", hi.xislice_h(it)); EXPECT_EQ("World", hi.xlslice(7, 5)); EXPECT_EQ("World", hi.xpslice(7, 12)); + EXPECT_EQ("World", hi.xislice(hi.begin() + 7, hi.begin() + 12)); EXPECT_TRUE(hi.startswith("Hello")); EXPECT_TRUE(hi.endswith("World!")); } @@ -52,8 +170,12 @@ TYPED_TEST_P(StringTest, oslice) EXPECT_EQ("Hello,", hi.oslice_h(6)); EXPECT_EQ("World!", hi.orslice_t(6)); EXPECT_EQ("Hello, ", hi.orslice_h(6)); + typename TypeParam::iterator it = std::find(hi.begin(), hi.end(), ' '); + EXPECT_EQ(" World!", hi.oislice_t(it)); + EXPECT_EQ("Hello,", hi.oislice_h(it)); EXPECT_EQ("World", hi.olslice(7, 5)); EXPECT_EQ("World", hi.opslice(7, 12)); + EXPECT_EQ("World", hi.oislice(hi.begin() + 7, hi.begin() + 12)); } TYPED_TEST_P(StringTest, convert) @@ -67,6 +189,8 @@ TYPED_TEST_P(StringTest, convert) ZString z = "z"; Xstring x = "x"; VString<255> v = "v"; + const char l[] = "l"; + VString<5> hi = "hello"; TypeParam f2 = f; TypeParam t2 = t; @@ -74,32 +198,40 @@ TYPED_TEST_P(StringTest, convert) TypeParam z2 = z; TypeParam x2 = x; TypeParam v2 = v; + TypeParam l2 = l; + TypeParam hi2 = hi; - EXPECT_EQ(f2, f); - EXPECT_EQ(t2, t); - EXPECT_EQ(s2, s); - EXPECT_EQ(z2, z); - EXPECT_EQ(x2, x); - EXPECT_EQ(v2, v); + EXPECT_EQ(f, f2); + EXPECT_EQ(t, t2); + EXPECT_EQ(s, s2); + EXPECT_EQ(z, z2); + EXPECT_EQ(x, x2); + EXPECT_EQ(v, v2); + EXPECT_EQ(l, l2); + EXPECT_EQ(hi, hi2); - TypeParam f3, t3, s3, z3, x3, v3; + TypeParam f3, t3, s3, z3, x3, v3, l3, hi3; f3 = f; t3 = t; s3 = s; z3 = z; x3 = x; v3 = v; + l3 = l; + hi3 = hi; - EXPECT_EQ(f3, f); - EXPECT_EQ(t3, t); - EXPECT_EQ(s3, s); - EXPECT_EQ(z3, z); - EXPECT_EQ(x3, x); - EXPECT_EQ(v3, v); + EXPECT_EQ(f, f3); + EXPECT_EQ(t, t3); + EXPECT_EQ(s, s3); + EXPECT_EQ(z, z3); + EXPECT_EQ(x, x3); + EXPECT_EQ(v, v3); + EXPECT_EQ(l, l3); + EXPECT_EQ(hi, hi3); } REGISTER_TYPED_TEST_CASE_P(StringTest, - basic, iterators, xslice, oslice, convert); + basic, order, iterators, xslice, oslice, convert); typedef ::testing::Types< FString, TString, SString, ZString, XString, VString<255> diff --git a/src/common/timer.cpp b/src/common/timer.cpp index ec1f6b2..c00f06d 100644 --- a/src/common/timer.cpp +++ b/src/common/timer.cpp @@ -184,10 +184,10 @@ interval_t do_timer(tick_t tick) return std::max(nextmin, std::chrono::milliseconds(10)); } -tick_t file_modified(const char *name) +tick_t file_modified(ZString name) { struct stat buf; - if (stat(name, &buf)) + if (stat(name.c_str(), &buf)) return tick_t(); return tick_t(std::chrono::seconds(buf.st_mtime)); } diff --git a/src/common/timer.hpp b/src/common/timer.hpp index c581377..b9c9588 100644 --- a/src/common/timer.hpp +++ b/src/common/timer.hpp @@ -5,6 +5,8 @@ # include "sanity.hpp" +# include "strings.hpp" + // updated automatically when using milli_clock::now() // which is done only by core.cpp extern tick_t gettick_cache; @@ -20,6 +22,6 @@ tick_t gettick(void) interval_t do_timer(tick_t tick); /// Stat a file, and return its modification time, truncated to seconds. -tick_t file_modified(const char *name); +tick_t file_modified(ZString name); #endif // TIMER_HPP diff --git a/src/common/utils.cpp b/src/common/utils.cpp index 4e00808..c9c22b9 100644 --- a/src/common/utils.cpp +++ b/src/common/utils.cpp @@ -5,120 +5,97 @@ #include -#include "../poison.hpp" - -//----------------------------------------------------- -// Function to suppress control characters in a string. -//----------------------------------------------------- -int remove_control_chars(char *str) -{ - int i; - int change = 0; - - for (i = 0; str[i]; i++) - { - if (0 <= str[i] && str[i] < 32) - { - str[i] = '_'; - change = 1; - } - } +#include "cxxstdio.hpp" +#include "extract.hpp" - return change; -} +#include "../poison.hpp" //--------------------------------------------------- // E-mail check: return 0 (not correct) or 1 (valid). //--------------------------------------------------- -int e_mail_check(const char *email) +bool e_mail_check(XString email) { - char ch; - const char *last_arobas; - // athena limits - if (strlen(email) < 3 || strlen(email) > 39) + if (email.size() < 3 || email.size() > 39) return 0; // part of RFC limits (official reference of e-mail description) - if (strchr(email, '@') == NULL || email[strlen(email) - 1] == '@') + XString::iterator at = std::find(email.begin(), email.end(), '@'); + if (at == email.end()) return 0; - - if (email[strlen(email) - 1] == '.') + XString username = email.xislice_h(at); + XString hostname = email.xislice_t(at + 1); + if (!username || !hostname) return 0; - - last_arobas = strrchr(email, '@'); - - if (strstr(last_arobas, "@.") != NULL || - strstr(last_arobas, "..") != NULL) + if (hostname.contains('@')) return 0; - - for (ch = 1; ch < 32; ch++) - { - if (strchr(last_arobas, ch) != NULL) - { - return 0; - } - } - - if (strchr(last_arobas, ' ') != NULL || - strchr(last_arobas, ';') != NULL) + if (hostname.front() == '.' || hostname.back() == '.') return 0; - - // all correct - return 1; + if (hostname.contains_seq("..")) + return 0; + if (email.contains_any(" ;")) + return 0; + return email.is_print(); } //------------------------------------------------- // Return numerical value of a switch configuration // on/off, english, français, deutsch, español //------------------------------------------------- -int config_switch (const char *str) +int config_switch (ZString str) { - if (strcasecmp(str, "on") == 0 || strcasecmp(str, "yes") == 0 - || strcasecmp(str, "oui") == 0 || strcasecmp(str, "ja") == 0 - || strcasecmp(str, "si") == 0) + if (str == "on" || str == "yes" + || str == "oui" || str == "ja" + || str == "si") return 1; - if (strcasecmp(str, "off") == 0 || strcasecmp(str, "no") == 0 - || strcasecmp(str, "non") == 0 || strcasecmp(str, "nein") == 0) + if (str == "off" || str == "no" + || str == "non" || str == "nein") return 0; - return atoi(str); + int rv; + if (extract(str, &rv)) + return rv; + FPRINTF(stderr, "Fatal: bad option value %s", str); + abort(); } -const char *ip2str(struct in_addr ip, bool extra_dot) +IP_String ip2str(struct in_addr ip) +{ + const uint8_t *p = reinterpret_cast(&ip); + + IP_String out; + SNPRINTF(out, 16, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + return out; +} +VString<16> ip2str_extradot(struct in_addr ip) { const uint8_t *p = reinterpret_cast(&ip); - static char buf[17]; - if (extra_dot) - sprintf(buf, "%d.%d.%d.%d.", p[0], p[1], p[2], p[3]); - else - sprintf(buf, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); - return buf; + VString<16> out; + SNPRINTF(out, 17, "%d.%d.%d.%d.", p[0], p[1], p[2], p[3]); + return out; } -bool split_key_value(const std::string& line, std::string *w1, std::string *w2) +bool split_key_value(const FString& line, SString *w1, TString *w2) { - std::string::const_iterator begin = line.begin(), end = line.end(); + FString::iterator begin = line.begin(), end = line.end(); - if (line[0] == '/' && line[1] == '/') + if (line.startswith("//")) return false; - if (line.back() == '\r') - --end; - if (line.empty()) + if (begin == end) return false; if (std::find_if(begin, end, [](unsigned char c) { return c < ' '; } ) != line.end()) return false; - std::string::const_iterator colon = std::find(begin, end, ':'); + FString::iterator colon = std::find(begin, end, ':'); if (colon == end) return false; - w1->assign(begin, colon); + *w1 = line.oislice(begin, colon); ++colon; while (std::isspace(*colon)) ++colon; - w2->assign(colon, end); + *w2 = line.oislice(colon, end); return true; } @@ -128,18 +105,22 @@ static_assert(sizeof(timestamp_milliseconds_buffer) == 24, "millis buffer"); void stamp_time(timestamp_seconds_buffer& out, const TimeT *t) { struct tm when = t ? *t : TimeT::now(); - strftime(out, 20, "%Y-%m-%d %H:%M:%S", &when); + char buf[20]; + strftime(buf, 20, "%Y-%m-%d %H:%M:%S", &when); + out = stringish(const_(buf)); } void stamp_time(timestamp_milliseconds_buffer& out) { struct timeval tv; gettimeofday(&tv, NULL); struct tm when = TimeT(tv.tv_sec); - strftime(out, 20, "%Y-%m-%d %H:%M:%S", &when); - sprintf(out + 19, ".%03d", int(tv.tv_usec / 1000)); + char buf[24]; + strftime(buf, 20, "%Y-%m-%d %H:%M:%S", &when); + sprintf(buf + 19, ".%03d", static_cast(tv.tv_usec / 1000)); + out = stringish(const_(buf)); } -void log_with_timestamp(FILE *out, const_string line) +void log_with_timestamp(FILE *out, XString line) { if (!line) { @@ -148,7 +129,7 @@ void log_with_timestamp(FILE *out, const_string line) } timestamp_milliseconds_buffer tmpstr; stamp_time(tmpstr); - fwrite(tmpstr, 1, sizeof(tmpstr), out); + fputs(tmpstr.c_str(), out); fputs(": ", out); fwrite(line.data(), 1, line.size(), out); if (line.back() != '\n') diff --git a/src/common/utils.hpp b/src/common/utils.hpp index ab32948..196bb3e 100644 --- a/src/common/utils.hpp +++ b/src/common/utils.hpp @@ -6,13 +6,15 @@ #include #include -#include #include #include "const_array.hpp" #include "operators.hpp" +#include "strings.hpp" #include "utils2.hpp" +struct IP_String : VString<15> {}; + template struct is_trivially_copyable : std::integral_constant {}; -int remove_control_chars(char *str); -int e_mail_check(const char *email); -int config_switch (const char *str); -const char *ip2str(struct in_addr ip, bool extra_dot = false); +bool e_mail_check(XString email); +int config_switch (ZString str); +IP_String ip2str(struct in_addr ip); +VString<15 + 1> ip2str_extradot(struct in_addr ip); -bool split_key_value(const std::string& line, std::string *w1, std::string *w2); - -inline -void strzcpy(char *dest, const char *src, size_t n) -{ - if (n) - { - // hmph - strncpy(dest, src, n - 1); - dest[n - 1] = '\0'; - } -} +bool split_key_value(const FString& line, SString *w1, TString *w2); inline void really_memcpy(uint8_t *dest, const uint8_t *src, size_t n) @@ -123,13 +114,14 @@ long long& convert_for_scanf(TimeT& t) return t.value; } -typedef char timestamp_seconds_buffer[20]; -typedef char timestamp_milliseconds_buffer[24]; +struct timestamp_seconds_buffer : VString<19> {}; +struct timestamp_milliseconds_buffer : VString<23> {}; void stamp_time(timestamp_seconds_buffer&, const TimeT *t=nullptr); void stamp_time(timestamp_milliseconds_buffer&); -void log_with_timestamp(FILE *out, const_string line); +void log_with_timestamp(FILE *out, XString line); +// TODO VString? #define TIMESTAMP_DUMMY "YYYY-MM-DD HH:MM:SS" static_assert(sizeof(TIMESTAMP_DUMMY) == sizeof(timestamp_seconds_buffer), "timestamp size"); @@ -138,6 +130,7 @@ static_assert(sizeof(TIMESTAMP_DUMMY) == sizeof(timestamp_seconds_buffer), // sizeof: 01234567890123456789012345678 // str + sizeof: ^ // -1: ^ +// there's probably a better way to do this now #define REPLACE_TIMESTAMP(str, t) \ stamp_time( \ reinterpret_cast( \ @@ -146,10 +139,4 @@ static_assert(sizeof(TIMESTAMP_DUMMY) == sizeof(timestamp_seconds_buffer), &t \ ) -template -const T& const_(T& t) -{ - return t; -} - #endif //UTILS_HPP diff --git a/src/common/utils2.hpp b/src/common/utils2.hpp index d7d6f8a..119cc13 100644 --- a/src/common/utils2.hpp +++ b/src/common/utils2.hpp @@ -248,4 +248,33 @@ typename std::enable_if::value, std::unique_ptr(new E[sz]()); } +template +const T& const_(T& t) +{ + return t; +} + +template +T no_cast(U&& u) +{ + typedef typename std::remove_reference::type Ti; + typedef typename std::remove_reference::type Ui; + typedef typename std::remove_cv::type Tb; + typedef typename std::remove_cv::type Ub; + static_assert(std::is_same::value, "not no cast"); + return std::forward(u); +} + +template +T base_cast(U&& u) +{ + static_assert(std::is_reference::value, "must base cast with references"); + typedef typename std::remove_reference::type Ti; + typedef typename std::remove_reference::type Ui; + typedef typename std::remove_cv::type Tb; + typedef typename std::remove_cv::type Ub; + static_assert(std::is_base_of::value, "not base cast"); + return std::forward(u); +} + #endif // UTILS2_HPP diff --git a/src/ladmin/ladmin.cpp b/src/ladmin/ladmin.cpp index 5f3ef7b..2581456 100644 --- a/src/ladmin/ladmin.cpp +++ b/src/ladmin/ladmin.cpp @@ -6,13 +6,17 @@ #include #include +#include -#include "../common/cxxstdio.hpp" #include "../common/core.hpp" +#include "../common/cxxstdio.hpp" +#include "../common/human_time_diff.hpp" +#include "../common/io.hpp" #include "../common/md5calc.hpp" +#include "../common/mmo.hpp" #include "../common/socket.hpp" -#include "../common/version.hpp" #include "../common/utils.hpp" +#include "../common/version.hpp" #include "../poison.hpp" @@ -32,15 +36,13 @@ int eathena_interactive_session; // (see login_athena.conf, 'admin_state' parameter) //------------------------------------------------------------------------- static -char loginserverip[16] = "127.0.0.1"; // IP of login-server +IP_String loginserverip = stringish("127.0.0.1"); // IP of login-server static int loginserverport = 6900; // Port of login-server static -char loginserveradminpassword[24] = "admin"; // Administration password +AccountPass loginserveradminpassword = stringish("admin"); // Administration password static -int passenc = 2; // Encoding type of the password -static -char ladmin_log_filename[1024] = "log/ladmin.log"; +FString ladmin_log_filename = "log/ladmin.log"; //------------------------------------------------------------------------- // LIST of COMMANDs that you can type at the prompt: // To use these commands you can only type only the first letters. @@ -228,7 +230,7 @@ int login_ip; static int bytes_to_read = 0; // flag to know if we waiting bytes from login-server static -char parameters[1024]; // needs to be global since it's passed to the parse function +TString parameters; // needs to be global since it's passed to the parse function // really should be added to session data static int list_first, list_last, list_type, list_count; // parameter to display a list of accounts @@ -247,311 +249,84 @@ void SessionDeleter::operator()(SessionData *) // Writing function of logs file //------------------------------ #define LADMIN_LOG(fmt, ...) \ - ladmin_log(static_cast(STRPRINTF(fmt, ## __VA_ARGS__))) + ladmin_log(STRPRINTF(fmt, ## __VA_ARGS__)) static -void ladmin_log(const_string line) +void ladmin_log(XString line) { - FILE *logfp = fopen_(ladmin_log_filename, "a"); + FILE *logfp = fopen(ladmin_log_filename.c_str(), "a"); if (!logfp) return; log_with_timestamp(logfp, line); - fclose_(logfp); -} - -//--------------------------------------------- -// Function to return ordonal text of a number. -//--------------------------------------------- -static -const char *makeordinal(int number) -{ - if ((number % 10) < 4 && (number % 10) != 0 - && (number < 10 || number > 20)) - { - if ((number % 10) == 1) - return "st"; - else if ((number % 10) == 2) - return "nd"; - else - return "rd"; - } - else - { - return "th"; - } -} - -//----------------------------------------------------------------------------------------- -// Function to test of the validity of an account name (return 0 if incorrect, and 1 if ok) -//----------------------------------------------------------------------------------------- -static -int verify_accountname(const char *account_name) -{ - int i; - - for (i = 0; account_name[i]; i++) - { - if (account_name[i] < 32) - { - PRINTF("Illegal character found in the account name (%d%s character).\n", - i + 1, makeordinal(i + 1)); - LADMIN_LOG("Illegal character found in the account name (%d%s character).\n", - i + 1, makeordinal(i + 1)); - return 0; - } - } - - if (strlen(account_name) < 4) - { - PRINTF("Account name is too short. Please input an account name of 4-23 bytes.\n"); - LADMIN_LOG("Account name is too short. Please input an account name of 4-23 bytes.\n"); - return 0; - } - - if (strlen(account_name) > 23) - { - PRINTF("Account name is too long. Please input an account name of 4-23 bytes.\n"); - LADMIN_LOG("Account name is too long. Please input an account name of 4-23 bytes.\n"); - return 0; - } - - return 1; + fclose(logfp); } -//---------------------------------- -// Sub-function: Input of a password -//---------------------------------- static -int typepasswd(char *password) +bool qsplit(ZString src) { - char password1[1023] {}; - char password2[1023] {}; - int letter; - int i; - - LADMIN_LOG("No password was given. Request to obtain a password.\n"); - - PRINTF("\033[1;36m Type the password > \033[0;32;42m"); - i = 0; - while ((letter = getchar()) != '\n') - password1[i++] = letter; - PRINTF("\033[0m\033[1;36m Verify the password > \033[0;32;42m"); - i = 0; - while ((letter = getchar()) != '\n') - password2[i++] = letter; - - PRINTF("\033[0m"); - fflush(stdout); - fflush(stdin); - - if (strcmp(password1, password2) != 0) - { - PRINTF("Password verification failed. Please input same password.\n"); - LADMIN_LOG("Password verification failed. Please input same password.\n"); - LADMIN_LOG(" First password: %s, second password: %s.\n", - password1, password2); - return 0; - } - LADMIN_LOG("Typed password: %s.\n", password1); - strcpy(password, password1); - return 1; + return !src; } -//------------------------------------------------------------------------------------ -// Sub-function: Test of the validity of password (return 0 if incorrect, and 1 if ok) -//------------------------------------------------------------------------------------ +template static -int verify_password(const char *password) +bool qsplit(ZString src, F first, R... rest) { - int i; - - for (i = 0; password[i]; i++) - { - if (password[i] < 32) - { - PRINTF("Illegal character found in the password (%d%s character).\n", - i + 1, makeordinal(i + 1)); - LADMIN_LOG("Illegal character found in the password (%d%s character).\n", - i + 1, makeordinal(i + 1)); - return 0; - } + if (!src) + return false; + XString one; + if (src.startswith('\'')) + { + src = src.xslice_t(1); + auto it = std::find(src.begin(), src.end(), '\''); + if (it == src.end()) + return false; + one = src.xislice_h(it); + src = src.xislice_t(it + 1); + while (src.startswith(' ')) + src = src.xslice_t(1); + } + else if (src.startswith('"')) + { + src = src.xslice_t(1); + auto it = std::find(src.begin(), src.end(), '"'); + if (it == src.end()) + return false; + one = src.xislice_h(it); + src = src.xislice_t(it + 1); + while (src.startswith(' ')) + src = src.xslice_t(1); } - - if (strlen(password) < 4) - { - PRINTF("Account name is too short. Please input an account name of 4-23 bytes.\n"); - LADMIN_LOG("Account name is too short. Please input an account name of 4-23 bytes.\n"); - return 0; - } - - if (strlen(password) > 23) + else { - PRINTF("Password is too long. Please input a password of 4-23 bytes.\n"); - LADMIN_LOG("Password is too long. Please input a password of 4-23 bytes.\n"); - return 0; + auto it = std::find(src.begin(), src.end(), ' '); + one = src.xislice_h(it); + src = src.xislice_t(it); + while (src.startswith(' ')) + src = src.xslice_t(1); } - - return 1; -} - -//------------------------------------------------------------------ -// Sub-function: Check the name of a command (return complete name) -//----------------------------------------------------------------- -static -int check_command(char *command) -{ -// help - if (strncmp(command, "help", 1) == 0 - && strncmp(command, "help", strlen(command)) == 0) - strcpy(command, "help"); -// general commands - else if (strncmp(command, "add", 2) == 0 && strncmp(command, "add", strlen(command)) == 0) - strcpy(command, "add"); - else if ((strncmp(command, "ban", 3) == 0 - && strncmp(command, "ban", strlen(command)) == 0) - ||(strncmp(command, "banish", 4) == 0 - && strncmp(command, "banish", strlen(command)) == 0)) - strcpy(command, "ban"); - else if ((strncmp(command, "banadd", 4) == 0 && strncmp(command, "banadd", strlen(command)) == 0) || // not 1 letter command: 'ba' or 'bs'? 'banadd' or 'banset' ? - strcmp(command, "ba") == 0) - strcpy(command, "banadd"); - else if ((strncmp(command, "banset", 4) == 0 && strncmp(command, "banset", strlen(command)) == 0) || // not 1 letter command: 'ba' or 'bs'? 'banadd' or 'banset' ? - strcmp(command, "bs") == 0) - strcpy(command, "banset"); - else if (strncmp(command, "block", 2) == 0 - && strncmp(command, "block", strlen(command)) == 0) - strcpy(command, "block"); - else if (strncmp(command, "check", 2) == 0 && strncmp(command, "check", strlen(command)) == 0) // not 1 letter command: 'check' or 'create'? - strcpy(command, "check"); - else if (strncmp(command, "create", 2) == 0 && strncmp(command, "create", strlen(command)) == 0) // not 1 letter command: 'check' or 'create'? - strcpy(command, "create"); - else if (strncmp(command, "delete", 1) == 0 - && strncmp(command, "delete", strlen(command)) == 0) - strcpy(command, "delete"); - else if ((strncmp(command, "email", 2) == 0 && strncmp(command, "email", strlen(command)) == 0) || // not 1 letter command: 'email', 'end' or 'exit'? - (strncmp(command, "e-mail", 2) == 0 - && strncmp(command, "e-mail", strlen(command)) == 0)) - strcpy(command, "email"); - else if (strncmp(command, "getcount", 2) == 0 && strncmp(command, "getcount", strlen(command)) == 0) // not 1 letter command: 'getcount' or 'gm'? - strcpy(command, "getcount"); -// else if (strncmp(command, "gm", 2) == 0 && strncmp(command, "gm", strlen(command)) == 0) // not 1 letter command: 'getcount' or 'gm'? -// strcpy(command, "gm"); -// else if (strncmp(command, "id", 2) == 0 && strncmp(command, "id", strlen(command)) == 0) // not 1 letter command: 'id' or 'info'? -// strcpy(command, "id"); - else if (strncmp(command, "info", 2) == 0 && strncmp(command, "info", strlen(command)) == 0) // not 1 letter command: 'id' or 'info'? - strcpy(command, "info"); -// else if (strncmp(command, "kami", 4) == 0 && strncmp(command, "kami", strlen(command)) == 0) // only all letters command: 'kami' or 'kamib'? -// strcpy(command, "kami"); -// else if (strncmp(command, "kamib", 5) == 0 && strncmp(command, "kamib", strlen(command)) == 0) // only all letters command: 'kami' or 'kamib'? -// strcpy(command, "kamib"); - else if ((strncmp(command, "list", 2) == 0 && strncmp(command, "list", strlen(command)) == 0) || // 'list' is default list command - strcmp(command, "ls") == 0) - strcpy(command, "list"); - else if (strncmp(command, "itemfrob", 6) == 0) - strcpy(command, "itemfrob"); - else if ((strncmp(command, "listban", 5) == 0 - && strncmp(command, "listban", strlen(command)) == 0) - ||(strncmp(command, "lsban", 3) == 0 - && strncmp(command, "lsban", strlen(command)) == 0) - || strcmp(command, "lb") == 0) - strcpy(command, "listban"); - else if ((strncmp(command, "listgm", 5) == 0 - && strncmp(command, "listgm", strlen(command)) == 0) - ||(strncmp(command, "lsgm", 3) == 0 - && strncmp(command, "lsgm", strlen(command)) == 0) - || strcmp(command, "lg") == 0) - strcpy(command, "listgm"); - else if ((strncmp(command, "listok", 5) == 0 - && strncmp(command, "listok", strlen(command)) == 0) - ||(strncmp(command, "lsok", 3) == 0 - && strncmp(command, "lsok", strlen(command)) == 0) - || strcmp(command, "lo") == 0) - strcpy(command, "listok"); - else if (strncmp(command, "memo", 1) == 0 - && strncmp(command, "memo", strlen(command)) == 0) - strcpy(command, "memo"); - else if (strncmp(command, "name", 1) == 0 - && strncmp(command, "name", strlen(command)) == 0) - strcpy(command, "name"); - else if ((strncmp(command, "password", 1) == 0 - && strncmp(command, "password", strlen(command)) == 0) - || strcmp(command, "passwd") == 0) - strcpy(command, "password"); - else if (strncmp(command, "reloadgm", 1) == 0 - && strncmp(command, "reloadgm", strlen(command)) == 0) - strcpy(command, "reloadgm"); - else if (strncmp(command, "search", 3) == 0 && strncmp(command, "search", strlen(command)) == 0) // not 1 letter command: 'search', 'state' or 'sex'? - strcpy(command, "search"); // not 2 letters command: 'search' or 'sex'? -// else if (strncmp(command, "sex", 3) == 0 && strncmp(command, "sex", strlen(command)) == 0) // not 1 letter command: 'search', 'state' or 'sex'? -// strcpy(command, "sex"); // not 2 letters command: 'search' or 'sex'? - else if (strncmp(command, "state", 2) == 0 && strncmp(command, "state", strlen(command)) == 0) // not 1 letter command: 'search', 'state' or 'sex'? - strcpy(command, "state"); - else if ((strncmp(command, "timeadd", 5) == 0 && strncmp(command, "timeadd", strlen(command)) == 0) || // not 1 letter command: 'ta' or 'ts'? 'timeadd' or 'timeset'? - strcmp(command, "ta") == 0) - strcpy(command, "timeadd"); - else if ((strncmp(command, "timeset", 5) == 0 && strncmp(command, "timeset", strlen(command)) == 0) || // not 1 letter command: 'ta' or 'ts'? 'timeadd' or 'timeset'? - strcmp(command, "ts") == 0) - strcpy(command, "timeset"); - else if ((strncmp(command, "unban", 5) == 0 - && strncmp(command, "unban", strlen(command)) == 0) - ||(strncmp(command, "unbanish", 4) == 0 - && strncmp(command, "unbanish", strlen(command)) == 0)) - strcpy(command, "unban"); - else if (strncmp(command, "unblock", 4) == 0 - && strncmp(command, "unblock", strlen(command)) == 0) - strcpy(command, "unblock"); - else if (strncmp(command, "version", 1) == 0 - && strncmp(command, "version", strlen(command)) == 0) - strcpy(command, "version"); - else if (strncmp(command, "who", 1) == 0 - && strncmp(command, "who", strlen(command)) == 0) - strcpy(command, "who"); -// quit - else if (strncmp(command, "quit", 1) == 0 - && strncmp(command, "quit", strlen(command)) == 0) - strcpy(command, "quit"); - else if (strncmp(command, "exit", 2) == 0 && strncmp(command, "exit", strlen(command)) == 0) // not 1 letter command: 'email', 'end' or 'exit'? - strcpy(command, "exit"); - else if (strncmp(command, "end", 2) == 0 && strncmp(command, "end", strlen(command)) == 0) // not 1 letter command: 'email', 'end' or 'exit'? - strcpy(command, "end"); - - return 0; + return extract(one, first) && qsplit(src, rest...); } //----------------------------------------- // Sub-function: Display commands of ladmin //----------------------------------------- static -void display_help(const char *param) +void display_help(ZString param) { - char command[1023] {}; - int i; - - if (sscanf(param, "%s ", command) < 1 || strlen(command) == 0) - strcpy(command, ""); // any value that is not a command - - if (command[0] == '?') - { - strcpy(command, "help"); - } + XString command = param.xislice_h(std::find(param.begin(), param.end(), ' ')); - // lowercase for command - for (i = 0; command[i]; i++) - command[i] = tolower(command[i]); - - // Analyse of the command - check_command(command); // give complete name to the command + if (command.startswith('?')) + command = "help"; LADMIN_LOG("Displaying of the commands or a command.\n"); - if (strcmp(command, "help") == 0) + if (command == "help") { PRINTF("help/?\n"); PRINTF(" Display the description of the commands\n"); PRINTF("help/? [command]\n"); PRINTF(" Display the description of the specified command\n"); -// general commands } - else if (strcmp(command, "add") == 0) + else if (command == "add") { PRINTF("add \n"); PRINTF(" Create an account with the default email (a@a.com).\n"); @@ -561,13 +336,13 @@ void display_help(const char *param) PRINTF(" the input is done without displaying of the pressed keys.\n"); PRINTF(" add testname Male testpass\n"); } - else if (strcmp(command, "ban") == 0) + else if (command == "ban") { PRINTF("ban/banish yyyy/mm/dd hh:mm:ss \n"); PRINTF(" Changes the final date of a banishment of an account.\n"); PRINTF(" Like banset, but is at end.\n"); } - else if (strcmp(command, "banadd") == 0) + else if (command == "banadd") { PRINTF("banadd \n"); PRINTF(" Adds or substracts time from the final date of a banishment of an account.\n"); @@ -586,7 +361,7 @@ void display_help(const char *param) PRINTF("NOTE: If you modify the final date of a non-banished account,\n"); PRINTF(" you fix the final date to (actual time +- adjustments)\n"); } - else if (strcmp(command, "banset") == 0) + else if (command == "banset") { PRINTF("banset yyyy/mm/dd [hh:mm:ss]\n"); PRINTF(" Changes the final date of a banishment of an account.\n"); @@ -594,13 +369,13 @@ void display_help(const char *param) PRINTF("banset 0\n"); PRINTF(" Set a non-banished account (0 = unbanished).\n"); } - else if (strcmp(command, "block") == 0) + else if (command == "block") { PRINTF("block \n"); PRINTF(" Set state 5 (You have been blocked by the GM Team) to an account.\n"); PRINTF(" This command works like state 5.\n"); } - else if (strcmp(command, "check") == 0) + else if (command == "check") { PRINTF("check \n"); PRINTF(" Check the validity of a password for an account.\n"); @@ -608,56 +383,56 @@ void display_help(const char *param) PRINTF(" It's the only method you have to know if a password is correct.\n"); PRINTF(" The other method is to have a ('physical') access to the accounts file.\n"); } - else if (strcmp(command, "create") == 0) + else if (command == "create") { PRINTF("create \n"); PRINTF(" Like the 'add' command, but with e-mail moreover.\n"); PRINTF(" create testname Male my@mail.com testpass\n"); } - else if (strcmp(command, "delete") == 0) + else if (command == "delete") { PRINTF("del \n"); PRINTF(" Remove an account.\n"); PRINTF(" This order requires confirmation. After confirmation, the account is deleted.\n"); } - else if (strcmp(command, "email") == 0) + else if (command == "email") { PRINTF("email \n"); PRINTF(" Modify the e-mail of an account.\n"); } - else if (strcmp(command, "getcount") == 0) + else if (command == "getcount") { PRINTF("getcount\n"); PRINTF(" Give the number of players online on all char-servers.\n"); } - else if (strcmp(command, "gm") == 0) + else if (command == "gm") { PRINTF("gm [GM_level]\n"); PRINTF(" Modify the GM level of an account.\n"); PRINTF(" Default value remove GM level (GM level = 0).\n"); PRINTF(" gm testname 80\n"); } - else if (strcmp(command, "id") == 0) + else if (command == "id") { PRINTF("id \n"); PRINTF(" Give the id of an account.\n"); } - else if (strcmp(command, "info") == 0) + else if (command == "info") { PRINTF("info \n"); PRINTF(" Display complete information of an account.\n"); } - else if (strcmp(command, "kami") == 0) + else if (command == "kami") { PRINTF("kami \n"); PRINTF(" Sends a broadcast message on all map-server (in yellow).\n"); } - else if (strcmp(command, "kamib") == 0) + else if (command == "kamib") { PRINTF("kamib \n"); PRINTF(" Sends a broadcast message on all map-server (in blue).\n"); } - else if (strcmp(command, "list") == 0) + else if (command == "list") { PRINTF("list/ls [start_id [end_id]]\n"); PRINTF(" Display a list of accounts.\n"); @@ -665,67 +440,64 @@ void display_help(const char *param) PRINTF(" Research by name is not possible with this command.\n"); PRINTF(" list 10 9999999\n"); } - else if (strcmp(command, "itemfrob") == 0) + else if (command == "itemfrob") { PRINTF("itemfrob \n"); PRINTF(" Translates item IDs for all accounts.\n"); PRINTF(" Any items matching the source item ID will be mapped to the dest-id.\n"); PRINTF(" itemfrob 500 700\n"); } - else if (strcmp(command, "listban") == 0) + else if (command == "listban") { PRINTF("listBan/lsBan [start_id [end_id]]\n"); PRINTF(" Like list/ls, but only for accounts with state or banished.\n"); } - else if (strcmp(command, "listgm") == 0) + else if (command == "listgm") { PRINTF("listGM/lsGM [start_id [end_id]]\n"); PRINTF(" Like list/ls, but only for GM accounts.\n"); } - else if (strcmp(command, "listok") == 0) + else if (command == "listok") { PRINTF("listOK/lsOK [start_id [end_id]]\n"); PRINTF(" Like list/ls, but only for accounts without state and not banished.\n"); } - else if (strcmp(command, "memo") == 0) + else if (command == "memo") { PRINTF("memo \n"); PRINTF(" Modify the memo of an account.\n"); PRINTF(" 'memo': it can have until 253 characters (with spaces or not).\n"); } - else if (strcmp(command, "name") == 0) + else if (command == "name") { PRINTF("name \n"); PRINTF(" Give the name of an account.\n"); } - else if (strcmp(command, "password") == 0) + else if (command == "password") { PRINTF("passwd \n"); PRINTF(" Change the password of an account.\n"); PRINTF(" When new password is omitted,\n"); PRINTF(" the input is done without displaying of the pressed keys.\n"); } - else if (strcmp(command, "reloadgm") == 0) + else if (command == "reloadgm") { PRINTF("reloadGM\n"); PRINTF(" Reload GM configuration file\n"); } - else if (strcmp(command, "search") == 0) + else if (command == "search") { PRINTF("search \n"); PRINTF(" Seek accounts.\n"); PRINTF(" Displays the accounts whose names correspond.\n"); -// PRINTF("search -r/-e/--expr/--regex \n"); -// PRINTF(" Seek accounts by regular expression.\n"); -// PRINTF(" Displays the accounts whose names correspond.\n"); } - else if (strcmp(command, "sex") == 0) + else if (command == "sex") { PRINTF("sex \n"); PRINTF(" Modify the sex of an account.\n"); PRINTF(" sex testname Male\n"); } - else if (strcmp(command, "state") == 0) + else if (command == "state") { PRINTF("state \n"); PRINTF(" Change the state of an account.\n"); @@ -746,7 +518,7 @@ void display_help(const char *param) PRINTF(" 'error_message_#7': message of the code error 6\n"); PRINTF(" = Your are Prohibited to log in until... (packet 0x006a)\n"); } - else if (strcmp(command, "timeadd") == 0) + else if (command == "timeadd") { PRINTF("timeadd \n"); PRINTF(" Adds or substracts time from the validity limit of an account.\n"); @@ -766,7 +538,7 @@ void display_help(const char *param) PRINTF(" If you want modify it, you want probably create a limited validity limit.\n"); PRINTF(" So, at first, you must set the validity limit to a date/time.\n"); } - else if (strcmp(command, "timeadd") == 0) + else if (command == "timeadd") { PRINTF("timeset yyyy/mm/dd [hh:mm:ss]\n"); PRINTF(" Changes the validity limit of an account.\n"); @@ -774,42 +546,40 @@ void display_help(const char *param) PRINTF("timeset 0\n"); PRINTF(" Gives an unlimited validity limit (0 = unlimited).\n"); } - else if (strcmp(command, "unban") == 0) + else if (command == "unban") { PRINTF("unban/unbanish \n"); PRINTF(" Remove the banishment of an account.\n"); PRINTF(" This command works like banset 0.\n"); } - else if (strcmp(command, "unblock") == 0) + else if (command == "unblock") { PRINTF("unblock \n"); PRINTF(" Set state 0 (Account ok) to an account.\n"); PRINTF(" This command works like state 0.\n"); } - else if (strcmp(command, "version") == 0) + else if (command == "version") { PRINTF("version\n"); PRINTF(" Display the version of the login-server.\n"); } - else if (strcmp(command, "who") == 0) + else if (command == "who") { PRINTF("who \n"); PRINTF(" Displays complete information of an account.\n"); -// quit } - else if (strcmp(command, "quit") == 0 || - strcmp(command, "exit") == 0 || - strcmp(command, "end") == 0) + else if (command == "quit" + || command == "exit" + || command == "end") { PRINTF("quit/end/exit\n"); PRINTF(" End of the program of administration.\n"); -// unknown command } else { - if (strlen(command) > 0) + if (command) PRINTF("Unknown command [%s] for help. Displaying of all commands.\n", - command); + FString(command)); PRINTF(" help/? -- Display this help\n"); PRINTF(" help/? [command] -- Display the help of the command\n"); PRINTF(" add -- Create an account with default email\n"); @@ -842,7 +612,6 @@ void display_help(const char *param) PRINTF(" quit/end/exit -- End of the program of administation\n"); PRINTF(" reloadGM -- Reload GM configuration file\n"); PRINTF(" search -- Seek accounts\n"); -// PRINTF(" search -e/-r/--expr/--regex -- Seek accounts by regular-expression\n"); PRINTF(" sex -- Modify the sex of an account\n"); PRINTF(" state -- Change the state\n"); PRINTF(" timeadd/ta -- Add or substract time from the\n"); @@ -862,195 +631,93 @@ void display_help(const char *param) // Sub-function: add an account //----------------------------- static -int addaccount(const char *param, int emailflag) +void addaccount(ZString param, int emailflag) { - char name[1023] {}; - char sex[1023] {}; - char email[1023] {}; - char password[1023] {}; + AccountName name; + VString<1> sex_; + XString email_; + AccountPass password; if (emailflag == 0) - { // add command - if (sscanf(param, "\"%[^\"]\" %s %[^\r\n]", name, sex, password) < 2 && // password can be void - sscanf(param, "'%[^']' %s %[^\r\n]", name, sex, password) < 2 && // password can be void - sscanf(param, "%s %s %[^\r\n]", name, sex, password) < 2) - { // password can be void + { + // add command + if (!qsplit(param, &name, &sex_, &password)) + { PRINTF("Please input an account name, a sex and a password.\n"); PRINTF(" add testname Male testpass\n"); LADMIN_LOG("Incomplete parameters to create an account ('add' command).\n"); - return 136; + return; } - strcpy(email, "a@a.com"); // default email + email_ = DEFAULT_EMAIL; } else - { // 1: create command - if (sscanf(param, "\"%[^\"]\" %s %s %[^\r\n]", name, sex, email, password) < 3 && // password can be void - sscanf(param, "'%[^']' %s %s %[^\r\n]", name, sex, email, password) < 3 && // password can be void - sscanf(param, "%s %s %s %[^\r\n]", name, sex, email, - password) < 3) - { // password can be void + { + // 1: create command + if (!qsplit(param, &name, &sex_, &email_, &password)) + { PRINTF("Please input an account name, a sex and a password.\n"); PRINTF(" create testname Male my@mail.com testpass\n"); LADMIN_LOG("Incomplete parameters to create an account ('create' command).\n"); - return 136; + return; } } - if (verify_accountname(name) == 0) - { - return 102; - } + char sex = sex_.front(); + if (!name.is_print()) + return; - sex[0] = toupper(sex[0]); - if (strchr("MF", sex[0]) == NULL) + if (XString("MF").contains(sex)) { - PRINTF("Illegal gender [%s]. Please input M or F.\n", sex); - LADMIN_LOG("Illegal gender [%s]. Please input M or F.\n", - sex); - return 103; + PRINTF("Illegal gender [%c]. Please input M or F.\n", sex); + LADMIN_LOG("Illegal gender [%c]. Please input M or F.\n", sex); + return; } - if (strlen(email) < 3) - { - PRINTF("Email is too short [%s]. Please input a valid e-mail.\n", - email); - LADMIN_LOG("Email is too short [%s]. Please input a valid e-mail.\n", - email); - return 109; - } - if (strlen(email) > 39) - { - PRINTF("Email is too long [%s]. Please input an e-mail with 39 bytes at the most.\n", - email); - LADMIN_LOG("Email is too long [%s]. Please input an e-mail with 39 bytes at the most.\n", - email); - return 109; - } - if (e_mail_check(email) == 0) + if (!e_mail_check(email_)) { PRINTF("Invalid email [%s]. Please input a valid e-mail.\n", - email); + FString(email_)); LADMIN_LOG("Invalid email [%s]. Please input a valid e-mail.\n", - email); - return 109; + FString(email_)); + return; } + AccountEmail email = stringish(email_); - if (strlen(password) == 0) - { - if (typepasswd(password) == 0) - return 108; - } - if (verify_password(password) == 0) - return 104; + if (!password.is_print()) + return; LADMIN_LOG("Request to login-server to create an account.\n"); WFIFOW(login_fd, 0) = 0x7930; WFIFO_STRING(login_fd, 2, name, 24); WFIFO_STRING(login_fd, 26, password, 24); - WFIFOB(login_fd, 50) = sex[0]; + WFIFOB(login_fd, 50) = sex; WFIFO_STRING(login_fd, 51, email, 40); WFIFOSET(login_fd, 91); bytes_to_read = 1; - - return 0; } //--------------------------------------------------------------------------------- // Sub-function: Add/substract time to the final date of a banishment of an account //--------------------------------------------------------------------------------- static -int banaddaccount(const char *param) +void banaddaccount(ZString param) { - char name[1023] {}; - char modif[1023] {}; - int year, month, day, hour, minute, second; - const char *p_modif; - int value, i; - - year = month = day = hour = minute = second = 0; + AccountName name; + HumanTimeDiff modif {}; - if (sscanf(param, "\"%[^\"]\" %[^\r\n]", name, modif) < 2 && - sscanf(param, "'%[^']' %[^\r\n]", name, modif) < 2 && - sscanf(param, "%s %[^\r\n]", name, modif) < 2) + if (!qsplit(param, &name, &modif)) { PRINTF("Please input an account name and a modifier.\n"); PRINTF(" : banadd testname +1m-2mn1s-6y\n"); PRINTF(" this example adds 1 month and 1 second, and substracts 2 minutes\n"); PRINTF(" and 6 years at the same time.\n"); LADMIN_LOG("Incomplete parameters to modify the ban date/time of an account ('banadd' command).\n"); - return 136; - } - if (verify_accountname(name) == 0) - { - return 102; - } - - // lowercase for modif - for (i = 0; modif[i]; i++) - modif[i] = tolower(modif[i]); - p_modif = modif; - while (strlen(p_modif) > 0) - { - value = atoi(p_modif); - if (value == 0) - { - p_modif++; - } - else - { - if (p_modif[0] == '-' || p_modif[0] == '+') - p_modif++; - while (strlen(p_modif) > 0 && p_modif[0] >= '0' - && p_modif[0] <= '9') - { - p_modif++; - } - if (p_modif[0] == 's') - { - second = value; - p_modif++; - } - else if (p_modif[0] == 'm' && p_modif[1] == 'n') - { - minute = value; - p_modif += 2; - } - else if (p_modif[0] == 'h') - { - hour = value; - p_modif++; - } - else if (p_modif[0] == 'd' || p_modif[0] == 'j') - { - day = value; - p_modif += 2; - } - else if (p_modif[0] == 'm') - { - month = value; - p_modif++; - } - else if (p_modif[0] == 'y' || p_modif[0] == 'a') - { - year = value; - p_modif++; - } - else - { - p_modif++; - } - } + return; } + if (!name.is_print()) + return; - PRINTF(" year: %d\n", year); - PRINTF(" month: %d\n", month); - PRINTF(" day: %d\n", day); - PRINTF(" hour: %d\n", hour); - PRINTF(" minute: %d\n", minute); - PRINTF(" second: %d\n", second); - - if (year == 0 && month == 0 && day == 0 && hour == 0 && minute == 0 - && second == 0) + if (!modif) { PRINTF("Please give an adjustment with this command:\n"); PRINTF(" Adjustment value (-1, 1, +1, etc...)\n"); @@ -1065,59 +732,16 @@ int banaddaccount(const char *param) PRINTF(" this example adds 1 month and 1 second, and substracts 2 minutes\n"); PRINTF(" and 6 years at the same time.\n"); LADMIN_LOG("No adjustment isn't an adjustment ('banadd' command).\n"); - return 137; - } - if (year > 127 || year < -127) - { - PRINTF("Please give a correct adjustment for the years (from -127 to 127).\n"); - LADMIN_LOG("Abnormal adjustement for the year ('banadd' command).\n"); - return 137; - } - if (month > 255 || month < -255) - { - PRINTF("Please give a correct adjustment for the months (from -255 to 255).\n"); - LADMIN_LOG("Abnormal adjustement for the month ('banadd' command).\n"); - return 137; - } - if (day > 32767 || day < -32767) - { - PRINTF("Please give a correct adjustment for the days (from -32767 to 32767).\n"); - LADMIN_LOG("Abnormal adjustement for the days ('banadd' command).\n"); - return 137; - } - if (hour > 32767 || hour < -32767) - { - PRINTF("Please give a correct adjustment for the hours (from -32767 to 32767).\n"); - LADMIN_LOG("Abnormal adjustement for the hours ('banadd' command).\n"); - return 137; - } - if (minute > 32767 || minute < -32767) - { - PRINTF("Please give a correct adjustment for the minutes (from -32767 to 32767).\n"); - LADMIN_LOG("Abnormal adjustement for the minutes ('banadd' command).\n"); - return 137; - } - if (second > 32767 || second < -32767) - { - PRINTF("Please give a correct adjustment for the seconds (from -32767 to 32767).\n"); - LADMIN_LOG("Abnormal adjustement for the seconds ('banadd' command).\n"); - return 137; + return; } LADMIN_LOG("Request to login-server to modify a ban date/time.\n"); WFIFOW(login_fd, 0) = 0x794c; WFIFO_STRING(login_fd, 2, name, 24); - WFIFOW(login_fd, 26) = year; - WFIFOW(login_fd, 28) = month; - WFIFOW(login_fd, 30) = day; - WFIFOW(login_fd, 32) = hour; - WFIFOW(login_fd, 34) = minute; - WFIFOW(login_fd, 36) = second; + WFIFO_STRUCT(login_fd, 24, modif); WFIFOSET(login_fd, 38); bytes_to_read = 1; - - return 0; } //----------------------------------------------------------------------- @@ -1125,7 +749,7 @@ int banaddaccount(const char *param) // Set the final date of a banishment of an account //----------------------------------------------------------------------- static -int bansetaccountsub(const char *name, const char *date, const char *time_) +void bansetaccountsub(AccountName name, XString date, XString time_) { int year, month, day, hour, minute, second; year = month = day = hour = minute = second = 0; @@ -1134,24 +758,22 @@ int bansetaccountsub(const char *name, const char *date, const char *time_) TimeT ban_until_time = TimeT(); struct tm tmtime = ban_until_time; // initialize - if (verify_accountname(name) == 0) - { - return 102; - } + if (!name.is_print()) + return; - if (atoi(date) != 0 && - ((sscanf(date, "%d/%d/%d", &year, &month, &day) < 3 && - sscanf(date, "%d-%d-%d", &year, &month, &day) < 3 && - sscanf(date, "%d.%d.%d", &year, &month, &day) < 3) || - sscanf(time_, "%d:%d:%d", &hour, &minute, &second) < 3)) + if (date != "0" + && ((!extract(date, record<'/'>(&year, &month, &day)) + && !extract(date, record<'-'>(&year, &month, &day)) + && !extract(date, record<'.'>(&year, &month, &day))) + || !extract(time_, record<':'>(&hour, &minute, &second)))) { PRINTF("Please input a date and a time (format: yyyy/mm/dd hh:mm:ss).\n"); PRINTF("You can imput 0 instead of if you use 'banset' command.\n"); LADMIN_LOG("Invalid format for the date/time ('banset' or 'ban' command).\n"); - return 102; + return; } - if (atoi(date) == 0) + if (date == "0") { ban_until_time = TimeT(); } @@ -1169,40 +791,40 @@ int bansetaccountsub(const char *name, const char *date, const char *time_) { PRINTF("Please give a correct value for the month (from 1 to 12).\n"); LADMIN_LOG("Invalid month for the date ('banset' or 'ban' command).\n"); - return 102; + return; } month = month - 1; if (day < 1 || day > 31) { PRINTF("Please give a correct value for the day (from 1 to 31).\n"); LADMIN_LOG("Invalid day for the date ('banset' or 'ban' command).\n"); - return 102; + return; } if (((month == 3 || month == 5 || month == 8 || month == 10) - && day > 30) ||(month == 1 && day > 29)) + && day > 30) || (month == 1 && day > 29)) { PRINTF("Please give a correct value for a day of this month (%d).\n", month); LADMIN_LOG("Invalid day for this month ('banset' or 'ban' command).\n"); - return 102; + return; } if (hour < 0 || hour > 23) { PRINTF("Please give a correct value for the hour (from 0 to 23).\n"); LADMIN_LOG("Invalid hour for the time ('banset' or 'ban' command).\n"); - return 102; + return; } if (minute < 0 || minute > 59) { PRINTF("Please give a correct value for the minutes (from 0 to 59).\n"); LADMIN_LOG("Invalid minute for the time ('banset' or 'ban' command).\n"); - return 102; + return; } if (second < 0 || second > 59) { PRINTF("Please give a correct value for the seconds (from 0 to 59).\n"); LADMIN_LOG("Invalid second for the time ('banset' or 'ban' command).\n"); - return 102; + return; } tmtime.tm_year = year; tmtime.tm_mon = month; @@ -1218,7 +840,7 @@ int bansetaccountsub(const char *name, const char *date, const char *time_) PRINTF("Please input a date and a time (format: yyyy/mm/dd hh:mm:ss).\n"); PRINTF("You can imput 0 instead of if you use 'banset' command.\n"); LADMIN_LOG("Invalid date. ('banset' or 'ban' command).\n"); - return 102; + return; } } @@ -1229,23 +851,19 @@ int bansetaccountsub(const char *name, const char *date, const char *time_) WFIFOL(login_fd, 26) = static_cast(ban_until_time); WFIFOSET(login_fd, 30); bytes_to_read = 1; - - return 0; } //--------------------------------------------------------------------- // Sub-function: Set the final date of a banishment of an account (ban) //--------------------------------------------------------------------- static -int banaccount(const char *param) +void banaccount(ZString param) { - char name[1023] {}; - char date[1023] {}; - char time_[1023] {}; + AccountName name; + XString date; + XString time_; - if (sscanf(param, "%s %s \"%[^\"]\"", date, time_, name) < 3 && - sscanf(param, "%s %s '%[^']'", date, time_, name) < 3 && - sscanf(param, "%s %s %[^\r\n]", date, time_, name) < 3) + if (!qsplit(param, &date, &time_, &name)) { PRINTF("Please input an account name, a date and a hour.\n"); PRINTF(": banset yyyy/mm/dd [hh:mm:ss]\n"); @@ -1254,26 +872,27 @@ int banaccount(const char *param) PRINTF(" unban/unbanish \n"); PRINTF(" Default time [hh:mm:ss]: 23:59:59.\n"); LADMIN_LOG("Incomplete parameters to set a ban ('banset' or 'ban' command).\n"); - return 136; + return; } - return bansetaccountsub(name, date, time_); + if (!time_) + time_ = "23:59:59"; + + bansetaccountsub(name, date, time_); } //------------------------------------------------------------------------ // Sub-function: Set the final date of a banishment of an account (banset) //------------------------------------------------------------------------ static -int bansetaccount(const char *param) +void bansetaccount(ZString param) { - char name[1023] {}; - char date[1023] {}; - char time_[1023] {}; - - if (sscanf(param, "\"%[^\"]\" %s %[^\r\n]", name, date, time_) < 2 && // if date = 0, time_ can be void - sscanf(param, "'%[^']' %s %[^\r\n]", name, date, time_) < 2 && // if date = 0, time_ can be void - sscanf(param, "%s %s %[^\r\n]", name, date, time_) < 2) - { // if date = 0, time_ can be void + AccountName name; + XString date; + XString time_; + + if (!qsplit(param, &name, &date, &time_)) + { PRINTF("Please input an account name, a date and a hour.\n"); PRINTF(": banset yyyy/mm/dd [hh:mm:ss]\n"); PRINTF(" banset 0 (0 = un-banished)\n"); @@ -1281,27 +900,24 @@ int bansetaccount(const char *param) PRINTF(" unban/unbanish \n"); PRINTF(" Default time [hh:mm:ss]: 23:59:59.\n"); LADMIN_LOG("Incomplete parameters to set a ban ('banset' or 'ban' command).\n"); - return 136; + return; } - if (time_[0] == '\0') - strcpy(time_, "23:59:59"); + if (!time_) + time_ = "23:59:59"; - return bansetaccountsub(name, date, time_); + bansetaccountsub(name, date, time_); } //------------------------------------------------- // Sub-function: unbanishment of an account (unban) //------------------------------------------------- static -int unbanaccount(const char *param) +void unbanaccount(ZString param) { - char name[1023] {}; + AccountName name; - if (strlen(param) == 0 || - (sscanf(param, "\"%[^\"]\"", name) < 1 && - sscanf(param, "'%[^']'", name) < 1 && - sscanf(param, "%[^\r\n]", name) < 1) || strlen(name) == 0) + if (!qsplit(param, &name)) { PRINTF("Please input an account name.\n"); PRINTF(": banset yyyy/mm/dd [hh:mm:ss]\n"); @@ -1310,10 +926,10 @@ int unbanaccount(const char *param) PRINTF(" unban/unbanish \n"); PRINTF(" Default time [hh:mm:ss]: 23:59:59.\n"); LADMIN_LOG("Incomplete parameters to set a ban ('unban' command).\n"); - return 136; + return; } - return bansetaccountsub(name, "0", ""); + bansetaccountsub(name, "0", ""); } //--------------------------------------------------------- @@ -1321,33 +937,24 @@ int unbanaccount(const char *param) // (Note: never send back a password with login-server!! security of passwords) //--------------------------------------------------------- static -int checkaccount(const char *param) +void checkaccount(ZString param) { - char name[1023] {}; - char password[1023] {}; + AccountName name; + AccountPass password; - if (sscanf(param, "\"%[^\"]\" %[^\r\n]", name, password) < 1 && // password can be void - sscanf(param, "'%[^']' %[^\r\n]", name, password) < 1 && // password can be void - sscanf(param, "%s %[^\r\n]", name, password) < 1) - { // password can be void + if (!qsplit(param, &name, &password)) + { PRINTF("Please input an account name.\n"); PRINTF(" check testname password\n"); LADMIN_LOG("Incomplete parameters to check the password of an account ('check' command).\n"); - return 136; + return; } - if (verify_accountname(name) == 0) - { - return 102; - } + if (!name.is_print()) + return; - if (strlen(password) == 0) - { - if (typepasswd(password) == 0) - return 134; - } - if (verify_password(password) == 0) - return 131; + if (!password.is_print()) + return; LADMIN_LOG("Request to login-server to check a password.\n"); @@ -1356,53 +963,47 @@ int checkaccount(const char *param) WFIFO_STRING(login_fd, 26, password, 24); WFIFOSET(login_fd, 50); bytes_to_read = 1; - - return 0; } //------------------------------------------------ // Sub-function: Asking for deletion of an account //------------------------------------------------ static -int delaccount(const char *param) +void delaccount(ZString param) { - char name[1023] {}; - char letter; - int i; + AccountName name; - if (strlen(param) == 0 || - (sscanf(param, "\"%[^\"]\"", name) < 1 && - sscanf(param, "'%[^']'", name) < 1 && - sscanf(param, "%[^\r\n]", name) < 1) || strlen(name) == 0) + if (!qsplit(param, &name)) { PRINTF("Please input an account name.\n"); PRINTF(" del testnametodelete\n"); LADMIN_LOG("No name given to delete an account ('delete' command).\n"); - return 136; + return; } - if (verify_accountname(name) == 0) - { - return 102; - } + if (!name.is_print()) + return; - char confirm[1023] {}; - while (confirm[0] != 'n' - && confirm[0] != 'y') + char confirm; + do { PRINTF("\033[1;36m ** Are you really sure to DELETE account [%s]? (y/n) > \033[0m", name); fflush(stdout); - strzcpy(confirm, "", sizeof(confirm)); - i = 0; - while ((letter = getchar()) != '\n') - confirm[i++] = letter; + int seek = getchar(); + confirm = seek; + if (seek == EOF) + confirm = 'n'; + else + while (seek != '\n' && seek != EOF) + seek = getchar(); } + while (!XString("yn").contains(confirm)); - if (confirm[0] == 'n') + if (confirm == 'n') { PRINTF("Deletion canceled.\n"); LADMIN_LOG("Deletion canceled by user ('delete' command).\n"); - return 121; + return; } LADMIN_LOG("Request to login-server to delete an acount.\n"); @@ -1411,58 +1012,36 @@ int delaccount(const char *param) WFIFO_STRING(login_fd, 2, name, 24); WFIFOSET(login_fd, 26); bytes_to_read = 1; - - return 0; } //---------------------------------------------------------- // Sub-function: Asking to modification of an account e-mail //---------------------------------------------------------- static -int changeemail(const char *param) +void changeemail(ZString param) { - char name[1023] {}; - char email[1023] {}; - - if (sscanf(param, "\"%[^\"]\" %[^\r\n]", name, email) < 2 && - sscanf(param, "'%[^']' %[^\r\n]", name, email) < 2 && - sscanf(param, "%s %[^\r\n]", name, email) < 2) + AccountName name; + XString email_; + if (!qsplit(param, &name, &email_)) { PRINTF("Please input an account name and an email.\n"); PRINTF(" email testname newemail\n"); LADMIN_LOG("Incomplete parameters to change the email of an account ('email' command).\n"); - return 136; + return; } - if (verify_accountname(name) == 0) - { - return 102; - } + if (!name.is_print()) + return; - if (strlen(email) < 3) - { - PRINTF("Email is too short [%s]. Please input a valid e-mail.\n", - email); - LADMIN_LOG("Email is too short [%s]. Please input a valid e-mail.\n", - email); - return 109; - } - if (strlen(email) > 39) - { - PRINTF("Email is too long [%s]. Please input an e-mail with 39 bytes at the most.\n", - email); - LADMIN_LOG("Email is too long [%s]. Please input an e-mail with 39 bytes at the most.\n", - email); - return 109; - } - if (e_mail_check(email) == 0) + if (!e_mail_check(email_)) { PRINTF("Invalid email [%s]. Please input a valid e-mail.\n", - email); + FString(email_)); LADMIN_LOG("Invalid email [%s]. Please input a valid e-mail.\n", - email); - return 109; + FString(email_)); + return; } + AccountEmail email = stringish(email_); LADMIN_LOG("Request to login-server to change an email.\n"); @@ -1471,48 +1050,40 @@ int changeemail(const char *param) WFIFO_STRING(login_fd, 26, email, 40); WFIFOSET(login_fd, 66); bytes_to_read = 1; - - return 0; } //----------------------------------------------------- // Sub-function: Asking of the number of online players //----------------------------------------------------- static -int getlogincount(void) +void getlogincount(void) { LADMIN_LOG("Request to login-server to obtain the # of online players.\n"); WFIFOW(login_fd, 0) = 0x7938; WFIFOSET(login_fd, 2); bytes_to_read = 1; - - return 0; } //---------------------------------------------------------- // Sub-function: Asking to modify the GM level of an account //---------------------------------------------------------- static -int changegmlevel(const char *param) +void changegmlevel(ZString param) { - char name[1023] {}; + AccountName name; int GM_level = 0; - if (sscanf(param, "\"%[^\"]\" %d", name, &GM_level) < 1 && - sscanf(param, "'%[^']' %d", name, &GM_level) < 1 && - sscanf(param, "%s %d", name, &GM_level) < 1) + if (!qsplit(param, &name, &GM_level)) { PRINTF("Please input an account name and a GM level.\n"); PRINTF(" gm testname 80\n"); LADMIN_LOG("Incomplete parameters to change the GM level of an account ('gm' command).\n"); - return 136; + return; } - if (verify_accountname(name) == 0) - { - return 102; - } + if (!name.is_print()) + return; if (GM_level < 0 || GM_level > 99) { @@ -1520,7 +1091,7 @@ int changegmlevel(const char *param) GM_level); LADMIN_LOG("Illegal GM level [%d]. The value can be from 0 to 99.\n", GM_level); - return 103; + return; } LADMIN_LOG("Request to login-server to change a GM level.\n"); @@ -1530,32 +1101,27 @@ int changegmlevel(const char *param) WFIFOB(login_fd, 26) = GM_level; WFIFOSET(login_fd, 27); bytes_to_read = 1; - - return 0; } //--------------------------------------------- // Sub-function: Asking to obtain an account id //--------------------------------------------- static -int idaccount(const char *param) +void idaccount(ZString param) { - char name[1023] {}; + AccountName name; - if (strlen(param) == 0 || - (sscanf(param, "\"%[^\"]\"", name) < 1 && - sscanf(param, "'%[^']'", name) < 1 && - sscanf(param, "%[^\r\n]", name) < 1) || strlen(name) == 0) + if (!qsplit(param, &name)) { PRINTF("Please input an account name.\n"); PRINTF(" id testname\n"); LADMIN_LOG("No name given to search an account id ('id' command).\n"); - return 136; + return; } - if (verify_accountname(name) == 0) + if (!name.is_print()) { - return 102; + return; } LADMIN_LOG("Request to login-server to know an account id.\n"); @@ -1564,21 +1130,19 @@ int idaccount(const char *param) WFIFO_STRING(login_fd, 2, name, 24); WFIFOSET(login_fd, 26); bytes_to_read = 1; - - return 0; } //---------------------------------------------------------------------------- // Sub-function: Asking to displaying information about an account (by its id) //---------------------------------------------------------------------------- static -int infoaccount(int account_id) +void infoaccount(int account_id) { if (account_id < 0) { PRINTF("Please input a positive value for the id.\n"); LADMIN_LOG("Negative value was given to found the account.\n"); - return 136; + return; } LADMIN_LOG("Request to login-server to obtain information about an account (by its id).\n"); @@ -1587,40 +1151,31 @@ int infoaccount(int account_id) WFIFOL(login_fd, 2) = account_id; WFIFOSET(login_fd, 6); bytes_to_read = 1; - - return 0; } //--------------------------------------- // Sub-function: Send a broadcast message //--------------------------------------- static -int sendbroadcast(short type, const char *message) +void sendbroadcast(ZString message) { - if (strlen(message) == 0) + if (!message) { PRINTF("Please input a message.\n"); - if (type == 0) { PRINTF(" kami a message\n"); } - else - { - PRINTF(" kamib a message\n"); - } - LADMIN_LOG("The message is void ('kami(b)' command).\n"); - return 136; + LADMIN_LOG("The message is void ('kami' command).\n"); + return; } WFIFOW(login_fd, 0) = 0x794e; - WFIFOW(login_fd, 2) = type; - size_t len = strlen(message) + 1; + WFIFOW(login_fd, 2) = 0; + size_t len = message.size() + 1; WFIFOL(login_fd, 4) = len; WFIFO_STRING(login_fd, 8, message, len); WFIFOSET(login_fd, 8 + len); bytes_to_read = 1; - - return 0; } @@ -1628,11 +1183,9 @@ int sendbroadcast(short type, const char *message) // Sub-function: Asking to Displaying of the accounts list //-------------------------------------------------------- static -int listaccount(char *param, int type) +void listaccount(ZString param, int type) { //int list_first, list_last, list_type; // parameter to display a list of accounts - int i; - list_type = type; // set default values @@ -1645,8 +1198,6 @@ int listaccount(char *param, int type) } else if (list_type == 2) { // if search - for (i = 0; param[i]; i++) - param[i] = tolower(param[i]); // get all accounts = use default } else if (list_type == 3) @@ -1658,23 +1209,13 @@ int listaccount(char *param, int type) // get all accounts = use default } else - { // if list (list_type == 0) - switch (sscanf(param, "%d %d", &list_first, &list_last)) - { - case 0: - // get all accounts = use default - break; - case 1: - list_last = 0; - // use tests of the following value - FALLTHROUGH; - default: - if (list_first < 0) - list_first = 0; - if (list_last < list_first || list_last < 0) - list_last = 0; - break; - } + { + // if list (list_type == 0) + extract(param, record<' '>(&list_first, &list_last)); + if (list_first < 0) + list_first = 0; + if (list_last < list_first || list_last < 0) + list_last = 0; } LADMIN_LOG("Request to login-server to obtain the list of accounts from %d to %d.\n", @@ -1690,19 +1231,17 @@ int listaccount(char *param, int type) Iprintf("account_id GM user_name sex count state\n"); Iprintf("-------------------------------------------------------------------------------\n"); list_count = 0; - - return 0; } //-------------------------------------------------------- // Sub-function: Frobnicate items //-------------------------------------------------------- static -int itemfrob(const char *param) +int itemfrob(ZString param) { int source_id, dest_id; - if (sscanf(param, "%d %d", &source_id, &dest_id) < 2) + if (!extract(param, record<' '>(&source_id, &dest_id))) { PRINTF("You must provide the source and destination item IDs.\n"); return 1; @@ -1721,27 +1260,23 @@ int itemfrob(const char *param) // Sub-function: Asking to modify a memo field //-------------------------------------------- static -int changememo(const char *param) +void changememo(ZString param) { - char name[1023] {}; - char memo[1023] {}; + AccountName name; + XString memo; - if (sscanf(param, "\"%[^\"]\" %[^\r\n]", name, memo) < 1 && // memo can be void - sscanf(param, "'%[^']' %[^\r\n]", name, memo) < 1 && // memo can be void - sscanf(param, "%s %[^\r\n]", name, memo) < 1) - { // memo can be void + if (!qsplit(param, &name, &memo) && !qsplit(param, &name)) + { PRINTF("Please input an account name and a memo.\n"); PRINTF(" memo testname new memo\n"); LADMIN_LOG("Incomplete parameters to change the memo of an account ('email' command).\n"); - return 136; + return; } - if (verify_accountname(name) == 0) - { - return 102; - } + if (!name.is_print()) + return; - size_t len = strlen(memo); + size_t len = memo.size(); size_t len1 = len + 1; if (len > 254) { @@ -1749,7 +1284,7 @@ int changememo(const char *param) PRINTF("Please input a memo of 254 bytes at the maximum.\n"); LADMIN_LOG("Email is too long (%zu characters). Please input a memo of 254 bytes at the maximum.\n", len); - return 102; + return; } LADMIN_LOG("Request to login-server to change a memo.\n"); @@ -1760,21 +1295,19 @@ int changememo(const char *param) WFIFO_STRING(login_fd, 28, memo, len); WFIFOSET(login_fd, 28 + len1); bytes_to_read = 1; - - return 0; } //----------------------------------------------- // Sub-function: Asking to obtain an account name //----------------------------------------------- static -int nameaccount(int id) +void nameaccount(int id) { if (id < 0) { PRINTF("Please input a positive value for the id.\n"); LADMIN_LOG("Negativ id given to search an account name ('name' command).\n"); - return 136; + return; } LADMIN_LOG("Request to login-server to know an account name.\n"); @@ -1783,8 +1316,6 @@ int nameaccount(int id) WFIFOL(login_fd, 2) = id; WFIFOSET(login_fd, 6); bytes_to_read = 1; - - return 0; } //------------------------------------------ @@ -1792,33 +1323,26 @@ int nameaccount(int id) // (Note: never send back a password with login-server!! security of passwords) //------------------------------------------ static -int changepasswd(const char *param) +void changepasswd(ZString param) { - char name[1023] {}; - char password[1023] {}; + AccountName name; + AccountPass password; - if (sscanf(param, "\"%[^\"]\" %[^\r\n]", name, password) < 1 && - sscanf(param, "'%[^']' %[^\r\n]", name, password) < 1 && - sscanf(param, "%s %[^\r\n]", name, password) < 1) + if (!qsplit(param, &name, &password)) { PRINTF("Please input an account name.\n"); PRINTF(" passwd testname newpassword\n"); LADMIN_LOG("Incomplete parameters to change the password of an account ('password' command).\n"); - return 136; + return; } - if (verify_accountname(name) == 0) + if (!name.is_print()) { - return 102; + return; } - if (strlen(password) == 0) - { - if (typepasswd(password) == 0) - return 134; - } - if (verify_password(password) == 0) - return 131; + if (!password.is_print()) + return; LADMIN_LOG("Request to login-server to change a password.\n"); @@ -1827,8 +1351,6 @@ int changepasswd(const char *param) WFIFO_STRING(login_fd, 26, password, 24); WFIFOSET(login_fd, 50); bytes_to_read = 1; - - return 0; } //---------------------------------------------------------------------- @@ -1836,7 +1358,7 @@ int changepasswd(const char *param) // this function have no answer //---------------------------------------------------------------------- static -int reloadGM(char *params) +void reloadGM(ZString params) { WFIFOW(login_fd, 0) = 0x7955; WFIFOSET(login_fd, 2); @@ -1846,52 +1368,45 @@ int reloadGM(char *params) PRINTF("Request to reload the GM configuration file sended.\n"); PRINTF("Check the actual GM accounts (after reloading):\n"); listaccount(params, 1); // 1: to list only GM - - return 180; } //----------------------------------------------------- // Sub-function: Asking to modify the sex of an account //----------------------------------------------------- static -int changesex(const char *param) +void changesex(ZString param) { - char name[1023] {}; - char sex[1023] {}; + AccountName name; + VString<1> sex_; - if (sscanf(param, "\"%[^\"]\" %[^\r\n]", name, sex) < 2 && - sscanf(param, "'%[^']' %[^\r\n]", name, sex) < 2 && - sscanf(param, "%s %[^\r\n]", name, sex) < 2) + if (!qsplit(param, &name, &sex_)) { PRINTF("Please input an account name and a sex.\n"); PRINTF(" sex testname Male\n"); LADMIN_LOG("Incomplete parameters to change the sex of an account ('sex' command).\n"); - return 136; + return; } + char sex = sex_.front(); - if (verify_accountname(name) == 0) + if (name.is_print()) { - return 102; + return; } - sex[0] = toupper(sex[0]); - if (strchr("MF", sex[0]) == NULL) + if (!XString("MF").contains(sex)) { - PRINTF("Illegal gender [%s]. Please input M or F.\n", sex); - LADMIN_LOG("Illegal gender [%s]. Please input M or F.\n", - sex); - return 103; + PRINTF("Illegal gender [%c]. Please input M or F.\n", sex); + LADMIN_LOG("Illegal gender [%c]. Please input M or F.\n", sex); + return; } LADMIN_LOG("Request to login-server to change a sex.\n"); WFIFOW(login_fd, 0) = 0x793c; WFIFO_STRING(login_fd, 2, name, 24); - WFIFOB(login_fd, 26) = sex[0]; + WFIFOB(login_fd, 26) = sex; WFIFOSET(login_fd, 27); bytes_to_read = 1; - - return 0; } //------------------------------------------------------------------------- @@ -1899,10 +1414,8 @@ int changesex(const char *param) // Asking to modify the state of an account //------------------------------------------------------------------------- static -int changestatesub(const char *name, int state, const char *error_message7) +void changestatesub(AccountName name, int state, XString error_message) { - const char *error_message = error_message7; - if ((state < 0 || state > 9) && state != 100) { // Valid values: 0: ok, or value of the 0x006a packet + 1 PRINTF("Please input one of these states:\n"); @@ -1917,13 +1430,11 @@ int changestatesub(const char *name, int state, const char *error_message7) PRINTF(" block \n"); PRINTF(" unblock \n"); LADMIN_LOG("Invalid value for the state of an account ('state', 'block' or 'unblock' command).\n"); - return 151; + return; } - if (verify_accountname(name) == 0) - { - return 102; - } + if (!name.is_print()) + return; if (state != 7) { @@ -1931,17 +1442,17 @@ int changestatesub(const char *name, int state, const char *error_message7) } else { - if (strlen(error_message) < 1) + if (error_message.size() < 1) { PRINTF("Error message is too short. Please input a message of 1-19 bytes.\n"); LADMIN_LOG("Error message is too short. Please input a message of 1-19 bytes.\n"); - return 102; + return; } - if (strlen(error_message) > 19) + if (error_message.size() > 19) { PRINTF("Error message is too long. Please input a message of 1-19 bytes.\n"); LADMIN_LOG("Error message is too long. Please input a message of 1-19 bytes.\n"); - return 102; + return; } } @@ -1953,23 +1464,19 @@ int changestatesub(const char *name, int state, const char *error_message7) WFIFO_STRING(login_fd, 30, error_message, 20); WFIFOSET(login_fd, 50); bytes_to_read = 1; - - return 0; } //------------------------------------------------------- // Sub-function: Asking to modify the state of an account //------------------------------------------------------- static -int changestate(const char *param) +void changestate(ZString param) { - char name[1023] {}; - char error_message[1023] {}; + AccountName name; int state; + XString error_message; - if (sscanf(param, "\"%[^\"]\" %d %[^\r\n]", name, &state, error_message) < 2 - && sscanf(param, "'%[^']' %d %[^\r\n]", name, &state, error_message) < 2 - && sscanf(param, "%s %d %[^\r\n]", name, &state, error_message) < 2) + if (!qsplit(param, &name, &state, &error_message) && !qsplit(param, &name, &state)) { PRINTF("Please input an account name and a state.\n"); PRINTF(" state testname 5\n"); @@ -1977,24 +1484,21 @@ int changestate(const char *param) PRINTF(" block \n"); PRINTF(" unblock \n"); LADMIN_LOG("Incomplete parameters to change the state of an account ('state' command).\n"); - return 136; + return; } - return changestatesub(name, state, error_message); + changestatesub(name, state, error_message); } //------------------------------------------- // Sub-function: Asking to unblock an account //------------------------------------------- static -int unblockaccount(const char *param) +void unblockaccount(ZString param) { - char name[1023] {}; + AccountName name; - if (strlen(param) == 0 || - (sscanf(param, "\"%[^\"]\"", name) < 1 && - sscanf(param, "'%[^']'", name) < 1 && - sscanf(param, "%[^\r\n]", name) < 1) || strlen(name) == 0) + if (!qsplit(param, &name)) { PRINTF("Please input an account name.\n"); PRINTF(" state testname 5\n"); @@ -2002,24 +1506,21 @@ int unblockaccount(const char *param) PRINTF(" block \n"); PRINTF(" unblock \n"); LADMIN_LOG("Incomplete parameters to change the state of an account ('unblock' command).\n"); - return 136; + return; } - return changestatesub(name, 0, "-"); // state 0, no error message + changestatesub(name, 0, "-"); // state 0, no error message } //------------------------------------------- // Sub-function: Asking to unblock an account //------------------------------------------- static -int blockaccount(const char *param) +void blockaccount(ZString param) { - char name[1023] {}; + AccountName name; - if (strlen(param) == 0 || - (sscanf(param, "\"%[^\"]\"", name) < 1 && - sscanf(param, "'%[^']'", name) < 1 && - sscanf(param, "%[^\r\n]", name) < 1) || strlen(name) == 0) + if (!qsplit(param, &name)) { PRINTF("Please input an account name.\n"); PRINTF(" state testname 5\n"); @@ -2027,108 +1528,36 @@ int blockaccount(const char *param) PRINTF(" block \n"); PRINTF(" unblock \n"); LADMIN_LOG("Incomplete parameters to change the state of an account ('block' command).\n"); - return 136; + return; } - return changestatesub(name, 5, "-"); // state 5, no error message + changestatesub(name, 5, "-"); // state 5, no error message } //--------------------------------------------------------------------- // Sub-function: Add/substract time to the validity limit of an account //--------------------------------------------------------------------- static -int timeaddaccount(const char *param) +void timeaddaccount(ZString param) { - char name[1023] {}; - char modif[1023] {}; - int year, month, day, hour, minute, second; - const char *p_modif; - int value, i; + AccountName name; + HumanTimeDiff modif {}; - year = month = day = hour = minute = second = 0; - - if (sscanf(param, "\"%[^\"]\" %[^\r\n]", name, modif) < 2 && - sscanf(param, "'%[^']' %[^\r\n]", name, modif) < 2 && - sscanf(param, "%s %[^\r\n]", name, modif) < 2) + if (!qsplit(param, &name, &modif)) { PRINTF("Please input an account name and a modifier.\n"); PRINTF(" : timeadd testname +1m-2mn1s-6y\n"); PRINTF(" this example adds 1 month and 1 second, and substracts 2 minutes\n"); PRINTF(" and 6 years at the same time.\n"); LADMIN_LOG("Incomplete parameters to modify a limit time ('timeadd' command).\n"); - return 136; - } - if (verify_accountname(name) == 0) - { - return 102; + return; } - - // lowercase for modif - for (i = 0; modif[i]; i++) - modif[i] = tolower(modif[i]); - p_modif = modif; - while (strlen(p_modif) > 0) + if (name.is_print()) { - value = atoi(p_modif); - if (value == 0) - { - p_modif++; - } - else - { - if (p_modif[0] == '-' || p_modif[0] == '+') - p_modif++; - while (strlen(p_modif) > 0 && p_modif[0] >= '0' - && p_modif[0] <= '9') - { - p_modif++; - } - if (p_modif[0] == 's') - { - second = value; - p_modif++; - } - else if (p_modif[0] == 'm' && p_modif[1] == 'n') - { - minute = value; - p_modif += 2; - } - else if (p_modif[0] == 'h') - { - hour = value; - p_modif++; - } - else if (p_modif[0] == 'd' || p_modif[0] == 'j') - { - day = value; - p_modif += 2; - } - else if (p_modif[0] == 'm') - { - month = value; - p_modif++; - } - else if (p_modif[0] == 'y' || p_modif[0] == 'a') - { - year = value; - p_modif++; - } - else - { - p_modif++; - } - } + return; } - PRINTF(" year: %d\n", year); - PRINTF(" month: %d\n", month); - PRINTF(" day: %d\n", day); - PRINTF(" hour: %d\n", hour); - PRINTF(" minute: %d\n", minute); - PRINTF(" second: %d\n", second); - - if (year == 0 && month == 0 && day == 0 && hour == 0 && minute == 0 - && second == 0) + if (!modif) { PRINTF("Please give an adjustment with this command:\n"); PRINTF(" Adjustment value (-1, 1, +1, etc...)\n"); @@ -2143,70 +1572,27 @@ int timeaddaccount(const char *param) PRINTF(" this example adds 1 month and 1 second, and substracts 2 minutes\n"); PRINTF(" and 6 years at the same time.\n"); LADMIN_LOG("No adjustment isn't an adjustment ('timeadd' command).\n"); - return 137; - } - if (year > 127 || year < -127) - { - PRINTF("Please give a correct adjustment for the years (from -127 to 127).\n"); - LADMIN_LOG("Abnormal adjustement for the year ('timeadd' command).\n"); - return 137; - } - if (month > 255 || month < -255) - { - PRINTF("Please give a correct adjustment for the months (from -255 to 255).\n"); - LADMIN_LOG("Abnormal adjustement for the month ('timeadd' command).\n"); - return 137; - } - if (day > 32767 || day < -32767) - { - PRINTF("Please give a correct adjustment for the days (from -32767 to 32767).\n"); - LADMIN_LOG("Abnormal adjustement for the days ('timeadd' command).\n"); - return 137; - } - if (hour > 32767 || hour < -32767) - { - PRINTF("Please give a correct adjustment for the hours (from -32767 to 32767).\n"); - LADMIN_LOG("Abnormal adjustement for the hours ('timeadd' command).\n"); - return 137; - } - if (minute > 32767 || minute < -32767) - { - PRINTF("Please give a correct adjustment for the minutes (from -32767 to 32767).\n"); - LADMIN_LOG("Abnormal adjustement for the minutes ('timeadd' command).\n"); - return 137; - } - if (second > 32767 || second < -32767) - { - PRINTF("Please give a correct adjustment for the seconds (from -32767 to 32767).\n"); - LADMIN_LOG("Abnormal adjustement for the seconds ('timeadd' command).\n"); - return 137; + return; } LADMIN_LOG("Request to login-server to modify a time limit.\n"); WFIFOW(login_fd, 0) = 0x7950; WFIFO_STRING(login_fd, 2, name, 24); - WFIFOW(login_fd, 26) = year; - WFIFOW(login_fd, 28) = month; - WFIFOW(login_fd, 30) = day; - WFIFOW(login_fd, 32) = hour; - WFIFOW(login_fd, 34) = minute; - WFIFOW(login_fd, 36) = second; + WFIFO_STRUCT(login_fd, 26, modif); WFIFOSET(login_fd, 38); bytes_to_read = 1; - - return 0; } //------------------------------------------------- // Sub-function: Set a validity limit of an account //------------------------------------------------- static -int timesetaccount(const char *param) +void timesetaccount(ZString param) { - char name[1023] {}; - char date[1023] {}; - char time_[1023] {}; + AccountName name; + XString date; + XString time_; int year, month, day, hour, minute, second; year = month = day = hour = minute = second = 0; @@ -2215,38 +1601,33 @@ int timesetaccount(const char *param) TimeT connect_until_time = TimeT(); struct tm tmtime = connect_until_time; // initialize - if (sscanf(param, "\"%[^\"]\" %s %[^\r\n]", name, date, time_) < 2 && // if date = 0, time_ can be void - sscanf(param, "'%[^']' %s %[^\r\n]", name, date, time_) < 2 && // if date = 0, time_ can be void - sscanf(param, "%s %s %[^\r\n]", name, date, time_) < 2) - { // if date = 0, time_ can be void + if (!qsplit(param, &name, &date, &time_)) + { PRINTF("Please input an account name, a date and a hour.\n"); PRINTF(": timeset yyyy/mm/dd [hh:mm:ss]\n"); PRINTF(" timeset 0 (0 = unlimited)\n"); PRINTF(" Default time [hh:mm:ss]: 23:59:59.\n"); LADMIN_LOG("Incomplete parameters to set a limit time ('timeset' command).\n"); - return 136; - } - if (verify_accountname(name) == 0) - { - return 102; + return; } + if (!name.is_print()) + return; - if (time_[0] == '\0') - strcpy(time_, "23:59:59"); + if (!time_) + time_ = "23:59:59"; - if (atoi(date) != 0 && - ((sscanf(date, "%d/%d/%d", &year, &month, &day) < 3 && - sscanf(date, "%d-%d-%d", &year, &month, &day) < 3 && - sscanf(date, "%d.%d.%d", &year, &month, &day) < 3 && - sscanf(date, "%d'%d'%d", &year, &month, &day) < 3) || - sscanf(time_, "%d:%d:%d", &hour, &minute, &second) < 3)) + if (date != "0" + && ((!extract(date, record<'/'>(&year, &month, &day)) + && !extract(date, record<'-'>(&year, &month, &day)) + && !extract(date, record<'.'>(&year, &month, &day))) + || !extract(time_, record<':'>(&hour, &minute, &second)))) { PRINTF("Please input 0 or a date and a time (format: 0 or yyyy/mm/dd hh:mm:ss).\n"); LADMIN_LOG("Invalid format for the date/time ('timeset' command).\n"); - return 102; + return; } - if (atoi(date) == 0) + if (date == "0") { connect_until_time = TimeT(); } @@ -2264,14 +1645,14 @@ int timesetaccount(const char *param) { PRINTF("Please give a correct value for the month (from 1 to 12).\n"); LADMIN_LOG("Invalid month for the date ('timeset' command).\n"); - return 102; + return; } month = month - 1; if (day < 1 || day > 31) { PRINTF("Please give a correct value for the day (from 1 to 31).\n"); LADMIN_LOG("Invalid day for the date ('timeset' command).\n"); - return 102; + return; } if (((month == 3 || month == 5 || month == 8 || month == 10) && day > 30) ||(month == 1 && day > 29)) @@ -2279,25 +1660,25 @@ int timesetaccount(const char *param) PRINTF("Please give a correct value for a day of this month (%d).\n", month); LADMIN_LOG("Invalid day for this month ('timeset' command).\n"); - return 102; + return; } if (hour < 0 || hour > 23) { PRINTF("Please give a correct value for the hour (from 0 to 23).\n"); LADMIN_LOG("Invalid hour for the time ('timeset' command).\n"); - return 102; + return; } if (minute < 0 || minute > 59) { PRINTF("Please give a correct value for the minutes (from 0 to 59).\n"); LADMIN_LOG("Invalid minute for the time ('timeset' command).\n"); - return 102; + return; } if (second < 0 || second > 59) { PRINTF("Please give a correct value for the seconds (from 0 to 59).\n"); LADMIN_LOG("Invalid second for the time ('timeset' command).\n"); - return 102; + return; } tmtime.tm_year = year; tmtime.tm_mon = month; @@ -2312,7 +1693,7 @@ int timesetaccount(const char *param) PRINTF("Invalid date.\n"); PRINTF("Please add 0 or a date and a time (format: 0 or yyyy/mm/dd hh:mm:ss).\n"); LADMIN_LOG("Invalid date. ('timeset' command).\n"); - return 102; + return; } } @@ -2323,31 +1704,26 @@ int timesetaccount(const char *param) WFIFOL(login_fd, 26) = static_cast(connect_until_time); WFIFOSET(login_fd, 30); bytes_to_read = 1; - - return 0; } //------------------------------------------------------------------------------ // Sub-function: Asking to displaying information about an account (by its name) //------------------------------------------------------------------------------ static -int whoaccount(const char *param) +void whoaccount(ZString param) { - char name[1023] {}; + AccountName name; - if (strlen(param) == 0 || - (sscanf(param, "\"%[^\"]\"", name) < 1 && - sscanf(param, "'%[^']'", name) < 1 && - sscanf(param, "%[^\r\n]", name) < 1) || strlen(name) == 0) + if (!qsplit(param, &name)) { PRINTF("Please input an account name.\n"); PRINTF(" who testname\n"); LADMIN_LOG("No name was given to found the account.\n"); - return 136; + return; } - if (verify_accountname(name) == 0) + if (!name.is_print()) { - return 102; + return; } LADMIN_LOG("Request to login-server to obtain information about an account (by its name).\n"); @@ -2356,23 +1732,19 @@ int whoaccount(const char *param) WFIFO_STRING(login_fd, 2, name, 24); WFIFOSET(login_fd, 26); bytes_to_read = 1; - - return 0; } //-------------------------------------------------------- // Sub-function: Asking of the version of the login-server //-------------------------------------------------------- static -int checkloginversion(void) +void checkloginversion(void) { LADMIN_LOG("Request to login-server to obtain its version.\n"); WFIFOW(login_fd, 0) = 0x7530; WFIFOSET(login_fd, 2); bytes_to_read = 1; - - return 0; } //--------------------------------------------- @@ -2381,12 +1753,8 @@ int checkloginversion(void) // and analyse the command. //--------------------------------------------- static -int prompt(void) +void prompt(void) { - int i, j; - char buf[1024]; - char *p; - // while we don't wait new packets while (bytes_to_read == 0) { @@ -2404,246 +1772,117 @@ int prompt(void) fflush(stdout); // get command and parameter - strzcpy(buf, "", sizeof(buf)); - fflush(stdin); - if (!fgets(buf, 1023, stdin)) - exit(0); - buf[1023] = '\0'; + FString buf; + io::getline(std::cin, buf); Iprintf("\033[0m"); fflush(stdout); - if (!eathena_interactive_session && !strlen(buf)) + if (!eathena_interactive_session && !buf) exit(0); - // remove final \n - if ((p = strrchr(buf, '\n')) != NULL) - p[0] = '\0'; - // remove all control char - for (i = 0; buf[i]; i++) - if (buf[i] < 32) - { - // remove cursor control. - if (buf[i] == 27 - && buf[i + 1] == '[' - && (buf[i + 2] == 'H' // home position (cursor) - || buf[i + 2] == 'J' // clear screen - || buf[i + 2] == 'A' // up 1 line - || buf[i + 2] == 'B' // down 1 line - || buf[i + 2] == 'C' // right 1 position - || buf[i + 2] == 'D' // left 1 position - || buf[i + 2] == 'G')) // center cursor (windows) - { - for (j = i; buf[j]; j++) - buf[j] = buf[j + 3]; - } - else if (buf[i] == 27 && buf[i + 1] == '[' - && buf[i + 2] == '2' && buf[i + 3] == 'J') - { - // clear screen - for (j = i; buf[j]; j++) - buf[j] = buf[j + 4]; - } - else if (buf[i] == 27 - && buf[i + 1] == '[' - && buf[i + 3] == '~' - && (buf[i + 2] == '1' // home (windows) - || buf[i + 2] == '2' // insert (windows) - || buf[i + 2] == '3' // del (windows) - || buf[i + 2] == '4' // end (windows) - || buf[i + 2] == '5' // pgup (windows) - || buf[i + 2] == '6')) // pgdown (windows) - { - for (j = i; buf[j]; j++) - buf[j] = buf[j + 4]; - } - else - { - // remove other control char. - for (j = i; buf[j]; j++) - buf[j] = buf[j + 1]; - } - i--; - } + if (!buf.is_print()) + { + printf("Cowardly refusing to execute a command that includes control or non-ascii characters\n"); + LADMIN_LOG("Cowardly refusing to execute a command that includes control or non-ascii characters\n"); + continue; + } - char command[1024] {}; // extract command name and parameters - strzcpy(parameters, "", sizeof(parameters)); - sscanf(buf, "%1023s %[^\n]", command, parameters); - command[1023] = '\0'; - parameters[1023] = '\0'; + auto space = std::find(buf.begin(), buf.end(), ' '); + FString command = buf.xislice_h(space); + while (*space == ' ') + ++space; - // lowercase for command line - for (i = 0; command[i]; i++) - command[i] = tolower(command[i]); + parameters = buf.xislice_t(space); - if (command[0] == '?' || strlen(command) == 0) - { - strcpy(buf, "help"); - strcpy(command, "help"); - } + if (!command || command.startswith('?')) + command = "help"; - // Analyse of the command - check_command(command); // give complete name to the command - - if (strlen(parameters) == 0) + if (!parameters) { LADMIN_LOG("Command: '%s' (without parameters)\n", - command); + command); } else { LADMIN_LOG("Command: '%s', parameters: '%s'\n", - command, parameters); + command, parameters); } // Analyse of the command -// help - if (strcmp(command, "help") == 0) - { + if (command == "help") display_help(parameters); -// general commands - } - else if (strcmp(command, "add") == 0) - { + else if (command == "add") addaccount(parameters, 0); // 0: no email - } - else if (strcmp(command, "ban") == 0) - { + else if (command == "ban") banaccount(parameters); - } - else if (strcmp(command, "banadd") == 0) - { + else if (command == "banadd") banaddaccount(parameters); - } - else if (strcmp(command, "banset") == 0) - { + else if (command == "banset") bansetaccount(parameters); - } - else if (strcmp(command, "block") == 0) - { + else if (command == "block") blockaccount(parameters); - } - else if (strcmp(command, "check") == 0) - { + else if (command == "check") checkaccount(parameters); - } - else if (strcmp(command, "create") == 0) - { + else if (command == "create") addaccount(parameters, 1); // 1: with email - } - else if (strcmp(command, "delete") == 0) - { + else if (command == "delete") delaccount(parameters); - } - else if (strcmp(command, "email") == 0) - { + else if (command == "email") changeemail(parameters); - } - else if (strcmp(command, "getcount") == 0) - { + else if (command == "getcount") getlogincount(); - } - else if (strcmp(command, "gm") == 0) - { + else if (command == "gm") changegmlevel(parameters); - } - else if (strcmp(command, "id") == 0) - { + else if (command == "id") idaccount(parameters); - } - else if (strcmp(command, "info") == 0) - { - infoaccount(atoi(parameters)); - } - else if (strcmp(command, "kami") == 0) - { - sendbroadcast(0, parameters); // flag for normal - } - else if (strcmp(command, "kamib") == 0) - { - sendbroadcast(0x10, parameters); // flag for blue - } - else if (strcmp(command, "itemfrob") == 0) - { + else if (command == "info") + infoaccount(atoi(parameters.c_str())); + else if (command == "kami") + sendbroadcast(parameters); // flag for normal + else if (command == "itemfrob") itemfrob(parameters); // 0: to list all - } - else if (strcmp(command, "list") == 0) - { + else if (command == "list") listaccount(parameters, 0); // 0: to list all - } - else if (strcmp(command, "listban") == 0) - { + else if (command == "listban") listaccount(parameters, 3); // 3: to list only accounts with state or bannished - } - else if (strcmp(command, "listgm") == 0) - { + else if (command == "listgm") listaccount(parameters, 1); // 1: to list only GM - } - else if (strcmp(command, "listok") == 0) - { + else if (command == "listok") listaccount(parameters, 4); // 4: to list only accounts without state and not bannished - } - else if (strcmp(command, "memo") == 0) - { + else if (command == "memo") changememo(parameters); - } - else if (strcmp(command, "name") == 0) - { - nameaccount(atoi(parameters)); - } - else if (strcmp(command, "password") == 0) - { + else if (command == "name") + nameaccount(atoi(parameters.c_str())); + else if (command == "password") changepasswd(parameters); - } - else if (strcmp(command, "reloadgm") == 0) - { + else if (command == "reloadgm") reloadGM(parameters); - } - else if (strcmp(command, "search") == 0) - { // no regex in C version + else if (command == "search") listaccount(parameters, 2); // 2: to list with pattern - } - else if (strcmp(command, "sex") == 0) - { + else if (command == "sex") changesex(parameters); - } - else if (strcmp(command, "state") == 0) - { + else if (command == "state") changestate(parameters); - } - else if (strcmp(command, "timeadd") == 0) - { + else if (command == "timeadd") timeaddaccount(parameters); - } - else if (strcmp(command, "timeset") == 0) - { + else if (command == "timeset") timesetaccount(parameters); - } - else if (strcmp(command, "unban") == 0) - { + else if (command == "unban") unbanaccount(parameters); - } - else if (strcmp(command, "unblock") == 0) - { + else if (command == "unblock") unblockaccount(parameters); - } - else if (strcmp(command, "version") == 0) - { + else if (command == "version") checkloginversion(); - } - else if (strcmp(command, "who") == 0) - { + else if (command == "who") whoaccount(parameters); -// quit - } - else if (strcmp(command, "quit") == 0 || - strcmp(command, "exit") == 0 || - strcmp(command, "end") == 0) + else if (command == "quit" + || command == "exit" + || command == "end") { PRINTF("Bye.\n"); exit(0); -// unknown command } else { @@ -2651,8 +1890,6 @@ int prompt(void) LADMIN_LOG("Unknown command [%s].\n", buf); } } - - return 0; } //------------------------------------------------------------- @@ -2702,39 +1939,6 @@ void parse_fromlogin(int fd) RFIFOSKIP(fd, 3); break; - case 0x01dc: // answer of a coding key request - if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd, 2)) - return; - { - char md5str[64] = ""; - size_t key_len = RFIFOW(fd, 2) - 4; - uint8_t md5bin[32]; - char md5key[key_len]; - RFIFO_STRING(fd, 4, md5key, key_len); - if (passenc == 1) - { - strcpy(md5str, md5key); - strcat(md5str, loginserveradminpassword); - } - else if (passenc == 2) - { - strcpy(md5str, loginserveradminpassword); - strcat(md5str, md5key); - } - MD5_to_bin(MD5_from_cstring(md5str), md5bin); - WFIFOW(login_fd, 0) = 0x7918; // Request for administation login (encrypted password) - WFIFOW(login_fd, 2) = passenc; // Encrypted type - really_memcpy(static_cast(WFIFOP(login_fd, 4)), md5bin, 16); - WFIFOSET(login_fd, 20); - Iprintf("Receiving of the MD5 key.\n"); - LADMIN_LOG("Receiving of the MD5 key.\n"); - Iprintf("Sending of the encrypted password...\n"); - LADMIN_LOG("Sending of the encrypted password...\n"); - } - bytes_to_read = 1; - RFIFOSKIP(fd, RFIFOW(fd, 2)); - break; - case 0x7531: // Displaying of the version of the login-server if (RFIFOREST(fd) < 10) return; @@ -2793,20 +1997,15 @@ void parse_fromlogin(int fd) LADMIN_LOG(" Receiving of a accounts list.\n"); for (i = 4; i < RFIFOW(fd, 2); i += 38) { - int j; - char userid[24]; - char lower_userid[24] {}; - RFIFO_STRING(fd, i + 5, userid, 24); - for (j = 0; userid[j]; j++) - lower_userid[j] = tolower(userid[j]); + AccountName userid = stringish(RFIFO_STRING<24>(fd, i + 5)); + VString<23> lower_userid = userid.to_lower(); list_first = RFIFOL(fd, i) + 1; // here are checks... - if (list_type == 0 || - (list_type == 1 && RFIFOB(fd, i + 4) > 0) || - (list_type == 2 - && strstr(lower_userid, parameters) != NULL) - ||(list_type == 3 && RFIFOL(fd, i + 34) != 0) - ||(list_type == 4 && RFIFOL(fd, i + 34) == 0)) + if (list_type == 0 + || (list_type == 1 && RFIFOB(fd, i + 4) > 0) + || (list_type == 2 && lower_userid.contains_seq(parameters)) + || (list_type == 3 && RFIFOL(fd, i + 34) != 0) + || (list_type == 4 && RFIFOL(fd, i + 34) == 0)) { PRINTF("%10d ", RFIFOL(fd, i)); if (RFIFOB(fd, i + 4) == 0) @@ -2880,8 +2079,7 @@ void parse_fromlogin(int fd) return; { int accid = RFIFOL(fd, 2); - char name[24]; - RFIFO_STRING(fd, 6, name, 24); + AccountName name = stringish(RFIFO_STRING<24>(fd, 6)); if (accid == -1) { PRINTF("Account [%s] creation failed. Same account already exists.\n", @@ -2906,8 +2104,7 @@ void parse_fromlogin(int fd) return; { int accid = RFIFOL(fd, 2); - char name[24]; - RFIFO_STRING(fd, 6, name, 24); + AccountName name = stringish(RFIFO_STRING<24>(fd, 6)); if (accid == -1) { PRINTF("Account [%s] deletion failed. Account doesn't exist.\n", @@ -2932,8 +2129,7 @@ void parse_fromlogin(int fd) return; { int accid = RFIFOL(fd, 2); - char name[24]; - RFIFO_STRING(fd, 6, name, 24); + AccountName name = stringish(RFIFO_STRING<24>(fd, 6)); if (accid == -1) { PRINTF("Account [%s] password changing failed.\n", @@ -2960,8 +2156,7 @@ void parse_fromlogin(int fd) return; { int accid = RFIFOL(fd, 2); - char name[24]; - RFIFO_STRING(fd, 6, name, 24); + AccountName name = stringish(RFIFO_STRING<24>(fd, 6)); int state = RFIFOL(fd, 30); if (accid == -1) { @@ -2972,7 +2167,8 @@ void parse_fromlogin(int fd) } else { - std::string tmpstr = STRPRINTF( + MString tmpstr; + tmpstr += STRPRINTF( "Account [%s] state successfully changed in [", name); switch (state) @@ -3012,8 +2208,9 @@ void parse_fromlogin(int fd) break; } tmpstr += ']'; - PRINTF("%s\n", tmpstr); - LADMIN_LOG("%s\n", tmpstr); + FString tmpstr_ = FString(tmpstr); + PRINTF("%s\n", tmpstr_); + LADMIN_LOG("%s\n", tmpstr_); } bytes_to_read = 0; } @@ -3037,8 +2234,7 @@ void parse_fromlogin(int fd) // Displaying of result for (int i = 4; i < RFIFOW(fd, 2); i += 32) { - char name[20]; - RFIFO_STRING(fd, i + 6, name, 20); + ServerName name = stringish(RFIFO_STRING<20>(fd, i + 6)); PRINTF(" %-20s : %5d\n", name, RFIFOW(fd, i + 26)); } @@ -3053,8 +2249,7 @@ void parse_fromlogin(int fd) return; { int account_id = RFIFOL(fd, 2); - char name[24]; - RFIFO_STRING(fd, 6, name, 24); + AccountName name = stringish(RFIFO_STRING<24>(fd, 6)); if (account_id == -1) { PRINTF("The account [%s] doesn't exist or the password is incorrect.\n", @@ -3079,8 +2274,7 @@ void parse_fromlogin(int fd) return; { int account_id = RFIFOL(fd, 2); - char name[24]; - RFIFO_STRING(fd, 6, name, 24); + AccountName name = stringish(RFIFO_STRING<24>(fd, 6)); if (account_id == -1) { PRINTF("Account [%s] sex changing failed.\n", @@ -3107,8 +2301,7 @@ void parse_fromlogin(int fd) return; { int account_id = RFIFOL(fd, 2); - char name[24]; - RFIFO_STRING(fd, 6, name, 24); + AccountName name = stringish(RFIFO_STRING<24>(fd, 6)); if (account_id == -1) { PRINTF("Account [%s] GM level changing failed.\n", @@ -3136,8 +2329,7 @@ void parse_fromlogin(int fd) return; { int account_id = RFIFOL(fd, 2); - char name[24]; - RFIFO_STRING(fd, 6, name, 24); + AccountName name = stringish(RFIFO_STRING<24>(fd, 6)); if (account_id == -1) { PRINTF("Account [%s] e-mail changing failed.\n", @@ -3164,8 +2356,7 @@ void parse_fromlogin(int fd) return; { int account_id = RFIFOL(fd, 2); - char name[24]; - RFIFO_STRING(fd, 6, name, 24); + AccountName name = stringish(RFIFO_STRING<24>(fd, 6)); if (account_id == -1) { PRINTF("Account [%s] memo changing failed. Account doesn't exist.\n", @@ -3190,8 +2381,7 @@ void parse_fromlogin(int fd) return; { int account_id = RFIFOL(fd, 2); - char name[24]; - RFIFO_STRING(fd, 6, name, 24); + AccountName name = stringish(RFIFO_STRING<24>(fd, 6)); if (account_id == -1) { PRINTF("Unable to find the account [%s] id. Account doesn't exist.\n", @@ -3216,9 +2406,8 @@ void parse_fromlogin(int fd) return; { int account_id = RFIFOL(fd, 2); - char name[24]; - RFIFO_STRING(fd, 6, name, 24); - if (strcmp(name, "") == 0) + AccountName name = stringish(RFIFO_STRING<24>(fd, 6)); + if (!name) { PRINTF("Unable to find the account [%d] name. Account doesn't exist.\n", account_id); @@ -3242,8 +2431,7 @@ void parse_fromlogin(int fd) return; { int account_id = RFIFOL(fd, 2); - char name[24]; - RFIFO_STRING(fd, 6, name, 24); + AccountName name = stringish(RFIFO_STRING<24>(fd, 6)); if (RFIFOL(fd, 2) == -1) { PRINTF("Account [%s] validity limit changing failed. Account doesn't exist.\n", @@ -3282,8 +2470,7 @@ void parse_fromlogin(int fd) return; { int account_id = RFIFOL(fd, 2); - char name[24]; - RFIFO_STRING(fd, 6, name, 24); + AccountName name = stringish(RFIFO_STRING<24>(fd, 6)); if (account_id == -1) { PRINTF("Account [%s] final date of banishment changing failed. Account doesn't exist.\n", @@ -3322,8 +2509,7 @@ void parse_fromlogin(int fd) return; { int account_id = RFIFOL(fd, 2); - char name[24]; - RFIFO_STRING(fd, 6, name, 24); + AccountName name = stringish(RFIFO_STRING<24>(fd, 6)); if (account_id == -1) { PRINTF("Account [%s] final date of banishment changing failed. Account doesn't exist.\n", @@ -3378,12 +2564,15 @@ void parse_fromlogin(int fd) case 0x7951: // answer of an account validity limit changing if (RFIFOREST(fd) < 34) return; - if (RFIFOL(fd, 2) == -1) + { + int account_id = RFIFOL(fd, 2); + AccountName name = stringish(RFIFO_STRING<24>(fd, 6)); + if (account_id == -1) { PRINTF("Account [%s] validity limit changing failed. Account doesn't exist.\n", - static_cast(RFIFOP(fd, 6))); + name); LADMIN_LOG("Account [%s] validity limit changing failed. Account doesn't exist.\n", - static_cast(RFIFOP(fd, 6))); + name); } else { @@ -3391,25 +2580,26 @@ void parse_fromlogin(int fd) if (!timestamp) { PRINTF("Validity limit of the account [%s][id: %d] unchanged.\n", - static_cast(RFIFOP(fd, 6)), RFIFOL(fd, 2)); + name, account_id); PRINTF("The account have an unlimited validity limit or\n"); PRINTF("the changing is impossible with the proposed adjustments.\n"); LADMIN_LOG("Validity limit of the account [%s][id: %d] unchanged. The account have an unlimited validity limit or the changing is impossible with the proposed adjustments.\n", - static_cast(RFIFOP(fd, 6)), RFIFOL(fd, 2)); + name, account_id); } else { timestamp_seconds_buffer tmpstr; stamp_time(tmpstr, ×tamp); PRINTF("Validity limit of the account [%s][id: %d] successfully changed to be until %s.\n", - static_cast(RFIFOP(fd, 6)), RFIFOL(fd, 2), + name, account_id, tmpstr); LADMIN_LOG("Validity limit of the account [%s][id: %d] successfully changed to be until %s.\n", - static_cast( RFIFOP(fd, 6)), RFIFOL(fd, 2), + name, account_id, tmpstr); } } bytes_to_read = 0; + } RFIFOSKIP(fd, 34); break; @@ -3418,54 +2608,55 @@ void parse_fromlogin(int fd) || RFIFOREST(fd) < (150 + RFIFOW(fd, 148))) return; { - char userid[24], error_message[20], lastlogin[24], - last_ip[16], email[40], memo[255]; - TimeT ban_until_time; // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban) - TimeT connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) - RFIFO_STRING(fd, 7, userid, 24); - RFIFO_STRING(fd, 40, error_message, 20); - RFIFO_STRING(fd, 60, lastlogin, 24); - RFIFO_STRING(fd, 84, last_ip, 16); - RFIFO_STRING(fd, 100, email, 40); - connect_until_time = static_cast(RFIFOL(fd, 140)); - ban_until_time = static_cast(RFIFOL(fd, 144)); - RFIFO_STRING(fd, 150, memo, RFIFOW(fd, 148)); - if (RFIFOL(fd, 2) == -1) + int account_id = RFIFOL(fd, 2); + uint8_t gm = RFIFOB(fd, 6); + AccountName userid = stringish(RFIFO_STRING<24>(fd, 7)); + uint8_t sex = RFIFOB(fd, 31); + int connections = RFIFOL(fd, 32); + int state = RFIFOL(fd, 36); + timestamp_seconds_buffer error_message = stringish(RFIFO_STRING<20>(fd, 40)); + timestamp_milliseconds_buffer lastlogin = stringish(RFIFO_STRING<24>(fd, 60)); + IP_String last_ip = stringish(RFIFO_STRING<16>(fd, 84)); + AccountEmail email = stringish(RFIFO_STRING<40>(fd, 100)); + TimeT connect_until_time = static_cast(RFIFOL(fd, 140)); + TimeT ban_until_time = static_cast(RFIFOL(fd, 144)); + FString memo = RFIFO_STRING(fd, 150, RFIFOW(fd, 148)); + if (account_id == -1) { PRINTF("Unabled to find the account [%s]. Account doesn't exist.\n", - parameters); + userid); LADMIN_LOG("Unabled to find the account [%s]. Account doesn't exist.\n", - parameters); + userid); } - else if (strlen(userid) == 0) + else if (!userid) { - PRINTF("Unabled to find the account [id: %s]. Account doesn't exist.\n", - parameters); - LADMIN_LOG("Unabled to find the account [id: %s]. Account doesn't exist.\n", - parameters); + PRINTF("Unabled to find the account [id: %d]. Account doesn't exist.\n", + account_id); + LADMIN_LOG("Unabled to find the account [id: %d]. Account doesn't exist.\n", + account_id); } else { LADMIN_LOG("Receiving information about an account.\n"); PRINTF("The account is set with:\n"); - if (RFIFOB(fd, 6) == 0) + if (!gm) { - PRINTF(" Id: %d (non-GM)\n", RFIFOL(fd, 2)); + PRINTF(" Id: %d (non-GM)\n", account_id); } else { PRINTF(" Id: %d (GM level %d)\n", - RFIFOL(fd, 2), RFIFOB(fd, 6)); + account_id, gm); } PRINTF(" Name: '%s'\n", userid); - if (RFIFOB(fd, 31) == 0) + if (sex == 0) PRINTF(" Sex: Female\n"); - else if (RFIFOB(fd, 31) == 1) + else if (sex == 1) PRINTF(" Sex: Male\n"); else PRINTF(" Sex: Server\n"); PRINTF(" E-mail: %s\n", email); - switch (RFIFOL(fd, 36)) + switch (state) { case 0: PRINTF(" Statut: 0 [Account OK]\n"); @@ -3500,7 +2691,7 @@ void parse_fromlogin(int fd) break; default: // 100 PRINTF(" Statut: %d [This ID is totally erased]\n", - RFIFOL(fd, 36)); + state); break; } if (!ban_until_time) @@ -3513,12 +2704,12 @@ void parse_fromlogin(int fd) stamp_time(tmpstr, &ban_until_time); PRINTF(" Banishment: until %s.\n", tmpstr); } - if (RFIFOL(fd, 32) > 1) + if (connections > 1) PRINTF(" Count: %d connections.\n", - RFIFOL(fd, 32)); + connections); else PRINTF(" Count: %d connection.\n", - RFIFOL(fd, 32)); + connections); PRINTF(" Last connection at: %s (ip: %s)\n", lastlogin, last_ip); if (!connect_until_time) @@ -3563,7 +2754,6 @@ int Connect_login_server(void) if ((login_fd = make_connection(login_ip, loginserverport)) < 0) return 0; - if (passenc == 0) { WFIFOW(login_fd, 0) = 0x7918; // Request for administation login WFIFOW(login_fd, 2) = 0; // no encrypted @@ -3574,14 +2764,6 @@ int Connect_login_server(void) Iprintf("Sending of the password...\n"); LADMIN_LOG("Sending of the password...\n"); } - else - { - WFIFOW(login_fd, 0) = 0x791a; // Sending request about the coding key - WFIFOSET(login_fd, 2); - bytes_to_read = 1; - Iprintf("Request about the MD5 key...\n"); - LADMIN_LOG("Request about the MD5 key...\n"); - } return 0; } @@ -3590,9 +2772,9 @@ int Connect_login_server(void) // Reading general configuration file //----------------------------------- static -int ladmin_config_read(const char *cfgName) +int ladmin_config_read(ZString cfgName) { - std::ifstream in(cfgName); + std::ifstream in(cfgName.c_str()); if (!in.is_open()) { PRINTF("\033[0mConfiguration file (%s) not found.\n", cfgName); @@ -3601,10 +2783,11 @@ int ladmin_config_read(const char *cfgName) Iprintf("\033[0m---Start reading of Ladmin configuration file (%s)\n", cfgName); - std::string line; - while (std::getline(in, line)) + FString line; + while (io::getline(in, line)) { - std::string w1, w2; + SString w1; + TString w2; if (!split_key_value(line, &w1, &w2)) continue; @@ -3619,14 +2802,14 @@ int ladmin_config_read(const char *cfgName) static_cast(h->h_addr[1]), static_cast(h->h_addr[2]), static_cast(h->h_addr[3])); - sprintf(loginserverip, "%d.%d.%d.%d", + SNPRINTF(loginserverip, 16, "%d.%d.%d.%d", static_cast(h->h_addr[0]), static_cast(h->h_addr[1]), static_cast(h->h_addr[2]), static_cast(h->h_addr[3])); } else - strzcpy(loginserverip, w2.c_str(), 16); + loginserverip = stringish(w2); } else if (w1 == "login_port") { @@ -3634,29 +2817,23 @@ int ladmin_config_read(const char *cfgName) } else if (w1 == "admin_pass") { - strzcpy(loginserveradminpassword, w2.c_str(), sizeof(loginserveradminpassword)); - } - else if (w1 == "passenc") - { - passenc = atoi(w2.c_str()); - if (passenc < 0 || passenc > 2) - passenc = 0; + loginserveradminpassword = stringish(w2); } else if (w1 == "ladmin_log_filename") { - strzcpy(ladmin_log_filename, w2.c_str(), sizeof(ladmin_log_filename)); + ladmin_log_filename = w2; } else if (w1 == "import") { - ladmin_config_read(w2.c_str()); + ladmin_config_read(w2); } else { - PRINTF("WARNING: unknown ladmin config key: %s\n", w1); + PRINTF("WARNING: unknown ladmin config key: %s\n", FString(w1)); } } - login_ip = inet_addr(loginserverip); + login_ip = inet_addr(loginserverip.c_str()); Iprintf("---End reading of Ladmin configuration file.\n"); @@ -3683,7 +2860,7 @@ void term_func(void) //------------------------ // Main function of ladmin //------------------------ -int do_init(int argc, char **argv) +int do_init(int argc, ZString *argv) { eathena_interactive_session = isatty(0); // read ladmin configuration diff --git a/src/login/login.cpp b/src/login/login.cpp index b3c7790..852cd3d 100644 --- a/src/login/login.cpp +++ b/src/login/login.cpp @@ -18,6 +18,8 @@ #include "../common/cxxstdio.hpp" #include "../common/db.hpp" #include "../common/extract.hpp" +#include "../common/human_time_diff.hpp" +#include "../common/io.hpp" #include "../common/lock.hpp" #include "../common/md5calc.hpp" #include "../common/mmo.hpp" @@ -39,21 +41,21 @@ constexpr int END_ACCOUNT_NUM = 100000000; struct mmo_account { - char userid[24]; - char passwd[24]; + AccountName userid; + AccountPass passwd; int passwdenc; long account_id; long login_id1; long login_id2; long char_id; - char lastlogin[24]; + timestamp_milliseconds_buffer lastlogin; int sex; }; struct mmo_char_server { - char name[20]; + ServerName name; long ip; short port; int users; @@ -70,24 +72,24 @@ int new_account_flag = 0; static int login_port = 6900; static -char lan_char_ip[16]; +IP_String lan_char_ip; static uint8_t subneti[4]; static uint8_t subnetmaski[4]; static -char update_host[128] = ""; +FString update_host; static -char main_server[20] = ""; +ServerName main_server; static -char account_filename[1024] = "save/account.txt"; +FString account_filename = "save/account.txt"; static -char GM_account_filename[1024] = "conf/GM_account.txt"; +FString GM_account_filename = "conf/GM_account.txt"; static -char login_log_filename[1024] = "log/login.log"; +FString login_log_filename = "log/login.log"; static -char login_log_unknown_packets_filename[1024] = "log/login_unknown_packets.log"; +FString login_log_unknown_packets_filename = "log/login_unknown_packets.log"; static int save_unknown_packets = 0; static @@ -124,7 +126,7 @@ enum class ACO }; // TODO: port the new code for this -typedef std::array AccessEntry; +struct AccessEntry : VString<127> {}; static ACO access_order = ACO::DENY_ALLOW; @@ -141,17 +143,13 @@ int start_limited_time = -1; // Starting additional sec from now for the limit static int check_ip_flag = 1; // It's to check IP of a player between login-server and char-server (part of anti-hacking system) - -struct login_session_data : SessionData -{ - int md5keylen; - char md5key[20]; -}; - -void SessionDeleter::operator()(SessionData *sd) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-noreturn" +void SessionDeleter::operator()(SessionData *) { - really_delete1 static_cast(sd); + assert(false && "login server does not have sessions anymore"); } +#pragma GCC diagnostic pop constexpr int AUTH_FIFO_SIZE = 256; struct @@ -165,15 +163,17 @@ int auth_fifo_pos = 0; struct AuthData { int account_id, sex; - char userid[24], pass[40], lastlogin[24]; + AccountName userid; + AccountCrypt pass; + timestamp_milliseconds_buffer lastlogin; int logincount; int state; // packet 0x006a value + 1 (0: compte OK) - char email[40]; // e-mail (by default: a@a.com) - char error_message[20]; // Message of error code #6 = Your are Prohibited to log in until %s (packet 0x006a) + AccountEmail email; // e-mail (by default: a@a.com) + timestamp_seconds_buffer error_message; // Message of error code #6 = Your are Prohibited to log in until %s (packet 0x006a) TimeT ban_until_time; // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban) TimeT connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) - char last_ip[16]; // save of last IP of connection - char memo[255]; // a memo field + IP_String last_ip; // save of last IP of connection + VString<254> memo; // a memo field int account_reg2_num; struct global_reg account_reg2[ACCOUNT_REG2_NUM]; }; @@ -183,9 +183,9 @@ std::vector auth_data; static int admin_state = 0; static -char admin_pass[24] = ""; +AccountPass admin_pass; static -char gm_pass[64] = ""; +FString gm_pass; static int level_new_gm = 60; @@ -213,15 +213,15 @@ using e::VERSION_2; // Writing function of logs file //------------------------------ #define LOGIN_LOG(fmt, ...) \ - login_log(static_cast(STRPRINTF(fmt, ## __VA_ARGS__))) + login_log(STRPRINTF(fmt, ## __VA_ARGS__)) static -void login_log(const_string line) +void login_log(XString line) { - FILE *logfp = fopen_(login_log_filename, "a"); + FILE *logfp = fopen(login_log_filename.c_str(), "a"); if (!logfp) return; log_with_timestamp(logfp, line); - fclose_(logfp); + fclose(logfp); } //---------------------------------------------------------------------- @@ -252,7 +252,7 @@ int read_gm_account(void) creation_time_GM_account_file = file_modified(GM_account_filename); - if ((fp = fopen_(GM_account_filename, "r")) == NULL) + if ((fp = fopen(GM_account_filename.c_str(), "r")) == NULL) { PRINTF("read_gm_account: GM accounts file [%s] not found.\n", GM_account_filename); @@ -266,8 +266,7 @@ int read_gm_account(void) // int (id) + int (level) = 8 bytes * 4000 = 32k (limit of packets in windows) while (fgets(line, sizeof(line) - 1, fp) && c < 4000) { - if ((line[0] == '/' && line[1] == '/') || line[0] == '\0' - || line[0] == '\n' || line[0] == '\r') + if ((line[0] == '/' && line[1] == '/') || line[0] == '\0' || line[0] == '\n') continue; GM_Account p {}; if (sscanf(line, "%d %hhu", &p.account_id, &p.level) != 2 @@ -310,7 +309,7 @@ int read_gm_account(void) } } } - fclose_(fp); + fclose(fp); PRINTF("read_gm_account: file '%s' readed (%d GM accounts found).\n", GM_account_filename, c); @@ -325,7 +324,7 @@ int read_gm_account(void) // (ip: IP to be tested, str: mask x.x.x.x/# or x.x.x.x/y.y.y.y) //-------------------------------------------------------------- static -int check_ipmask(struct in_addr ip, const char *str) +int check_ipmask(struct in_addr ip, ZString str) { unsigned int mask = 0, ip2; uint8_t *p = reinterpret_cast(&ip2), @@ -333,17 +332,17 @@ int check_ipmask(struct in_addr ip, const char *str) int i = 0; unsigned int m; - if (sscanf(str, "%hhu.%hhu.%hhu.%hhu/%n", + if (SSCANF(str, "%hhu.%hhu.%hhu.%hhu/%n", &p[0], &p[1], &p[2], &p[3], &i) != 4 || i == 0) return 0; - if (sscanf(str + i, "%hhu.%hhu.%hhu.%hhu", + if (SSCANF(str.oslice_t(i), "%hhu.%hhu.%hhu.%hhu", &p2[0], &p2[1], &p2[2], &p2[3]) == 4) { mask = ntohl(mask); } - else if (sscanf(str + i, "%u", &m) == 1 && m <= 32) + else if (SSCANF(str.oslice_t(i), "%u", &m) == 1 && m <= 32) { for (i = 0; i < m && i < 32; i++) mask = (mask >> 1) | 0x80000000; @@ -386,13 +385,12 @@ bool check_ip(struct in_addr ip) // If we have an answer, there is no guarantee to have a 100% correct value. // And, the waiting time (to check) can be long (over 1 minute to a timeout). That can block the software. // So, DNS notation isn't authorised for ip checking. - const char *buf = ip2str(ip, true); + VString<16> buf = ip2str_extradot(ip); for (const AccessEntry& ae : access_allow) { - const char *p = ae.data(); -#warning "TODO remove the strncmp part and use an IPAddress4 class" - if (strncmp(p, buf, strlen(p)) == 0 || check_ipmask(ip, p)) +#warning "TODO use an IPAddress4 and IPMask4 class" + if (buf.startswith(ae) || check_ipmask(ip, ae)) { flag = ACF::ALLOW; if (access_order == ACO::ALLOW_DENY) @@ -404,8 +402,7 @@ bool check_ip(struct in_addr ip) for (const AccessEntry& ae : access_deny) { - const char *p = ae.data(); - if (strncmp(p, buf, strlen(p)) == 0 || check_ipmask(ip, p)) + if (buf.startswith(ae) || check_ipmask(ip, ae)) { flag = ACF::DENY; return 0; @@ -439,12 +436,11 @@ bool check_ladminip(struct in_addr ip) // If we have an answer, there is no guarantee to have a 100% correct value. // And, the waiting time (to check) can be long (over 1 minute to a timeout). That can block the software. // So, DNS notation isn't authorised for ip checking. - const char *buf = ip2str(ip, true); + VString<16> buf = ip2str_extradot(ip); for (const AccessEntry& ae : access_ladmin) { - const char *p = ae.data(); - if (strncmp(p, buf, strlen(p)) == 0 || check_ipmask(ip, p)) + if (buf.startswith(ae) || check_ipmask(ip, ae)) return true; } @@ -454,34 +450,18 @@ bool check_ladminip(struct in_addr ip) //----------------------------------------------- // Search an account id // (return account pointer or nullptr (if not found)) -// If exact account name is not found, -// the function checks without case sensitive -// and returns index if only 1 account is found -// and similar to the searched name. //----------------------------------------------- static -AuthData *search_account(const char *account_name) +AuthData *search_account(AccountName account_name) { - int quantity = 0; - AuthData *index = nullptr; for (AuthData& ad : auth_data) { - // Without case sensitive check (increase the number of similar account names found) - if (strcasecmp(ad.userid, account_name) == 0) { - // Strict comparison (if found, we finish the function immediatly with correct value) - if (strcmp(ad.userid, account_name) == 0) + if (ad.userid == account_name) return &ad; - quantity++; - index = &ad; } } - // Here, the exact account name is not found - // We return the found index of a similar account ONLY if there is 1 similar account - if (quantity == 1) - return index; - // Exact account name is not found and 0 or more than 1 similar accounts have been found ==> we say not found return nullptr; } @@ -489,9 +469,10 @@ AuthData *search_account(const char *account_name) // Create a string to save the account in the account file //-------------------------------------------------------- static -std::string mmo_auth_tostr(const AuthData *p) +FString mmo_auth_tostr(const AuthData *p) { - std::string str = STRPRINTF( + MString str; + str += STRPRINTF( "%d\t" "%s\t" "%s\t" @@ -524,14 +505,14 @@ std::string mmo_auth_tostr(const AuthData *p) str += STRPRINTF("%s,%d ", p->account_reg2[i].str, p->account_reg2[i].value); - return str; + return FString(str); } static -bool extract(const_string line, AuthData *ad) +bool extract(XString line, AuthData *ad) { std::vector vars; - const_string sex = nullptr; // really only 1 char + VString<1> sex; if (!extract(line, record<'\t'>( &ad->account_id, @@ -555,19 +536,22 @@ bool extract(const_string line, AuthData *ad) { if (adi.account_id == ad->account_id) return false; - if (strcmp(adi.userid, ad->userid) == 0) + if (adi.userid == ad->userid) return false; } // If a password is not encrypted, we encrypt it now. // A password beginning with ! and - in the memo field is our magic - if (ad->pass[0] != '!' && ad->memo[0] == '-') { - strcpy(ad->pass, MD5_saltcrypt(ad->pass, make_salt())); - ad->memo[0] = '!'; + if (ad->pass[0] != '!' && ad->memo[0] == '-') + { + XString pass = ad->pass; + AccountPass plain = stringish(pass); + ad->pass = MD5_saltcrypt(plain, make_salt()); + ad->memo = '!'; } if (sex.size() != 1) return false; - switch(sex.front()) + switch (sex.front()) { case 'S': case 's': ad->sex = 2; break; case 'M': case 'm': ad->sex = 1; break; @@ -575,12 +559,12 @@ bool extract(const_string line, AuthData *ad) default: return false; } - if (e_mail_check(ad->email) == 0) - strzcpy(ad->email, "a@a.com", 40); + if (!e_mail_check(ad->email)) + ad->email = DEFAULT_EMAIL; - if (ad->error_message[0] == '\0' || ad->state != 7) + if (!ad->error_message || ad->state != 7) // 7, because state is packet 0x006a value + 1 - strzcpy(ad->error_message, "-", 20); + ad->error_message = stringish("-"); if (vars.size() > ACCOUNT_REG2_NUM) return false; @@ -599,7 +583,7 @@ int mmo_auth_init(void) int GM_count = 0; int server_count = 0; - std::ifstream in(account_filename); + std::ifstream in(account_filename.c_str()); if (!in.is_open()) { // no account file -> no account -> no login, including char-server (ERROR) @@ -608,19 +592,11 @@ int mmo_auth_init(void) return 0; } - std::string line; - while (std::getline(in, line)) + FString line; + while (io::getline(in, line)) { - if (line[0] == '/' && line[1] == '/') + if (line.startswith("//")) continue; - if (line.back() == '\r') - { -#ifdef WORKAROUND_GCC46_LIBRARY - line.resize(line.size() - 1); -#else - line.pop_back(); -#endif - } if (std::find_if(line.begin(), line.end(), [](unsigned char c) { return c < ' ' && c != '\t'; } ) != line.end()) @@ -649,7 +625,7 @@ int mmo_auth_init(void) account_id_count = ad.account_id + 1; } - std::string str = STRPRINTF("%s has %zu accounts (%d GMs, %d servers)\n", + FString str = STRPRINTF("%s has %zu accounts (%d GMs, %d servers)\n", account_filename, auth_data.size(), GM_count, server_count); PRINTF("%s: %s\n", __PRETTY_FUNCTION__, str); LOGIN_LOG("%s\n", line); @@ -696,7 +672,7 @@ void mmo_auth_sync(void) if (ad.account_id < 0) continue; - std::string line = mmo_auth_tostr(&ad); + FString line = mmo_auth_tostr(&ad); FPRINTF(fp, "%s\n", line); } FPRINTF(fp, "%d\t%%newid%%\n", account_id_count); @@ -804,7 +780,7 @@ void check_GM_file(TimerData *, tick_t) // Account creation (with e-mail check) //------------------------------------- static -int mmo_auth_new(struct mmo_account *account, char sex, const char *email) +int mmo_auth_new(struct mmo_account *account, char sex, AccountEmail email) { while (isGM(account_id_count) > 0) account_id_count++; @@ -812,19 +788,19 @@ int mmo_auth_new(struct mmo_account *account, char sex, const char *email) struct AuthData ad {}; ad.account_id = account_id_count++; - strzcpy(ad.userid, account->userid, 24); - strzcpy(ad.pass, MD5_saltcrypt(account->passwd, make_salt()), 40); - strcpy(ad.lastlogin, "-"); + ad.userid = account->userid; + ad.pass = MD5_saltcrypt(account->passwd, make_salt()); + ad.lastlogin = stringish("-"); ad.sex = (sex == 'M'); ad.logincount = 0; ad.state = 0; - if (e_mail_check(email) == 0) - strzcpy(ad.email, "a@a.com", 40); + if (!e_mail_check(email)) + ad.email = DEFAULT_EMAIL; else - strzcpy(ad.email, email, 40); + ad.email = email; - strcpy(ad.error_message, "-"); + ad.error_message = stringish("-"); ad.ban_until_time = TimeT(); if (start_limited_time < 0) @@ -838,8 +814,8 @@ int mmo_auth_new(struct mmo_account *account, char sex, const char *email) ad.connect_until_time = timestamp; } - strcpy(ad.last_ip, "-"); - strcpy(ad.memo, "!"); + ad.last_ip = stringish("-"); + ad.memo = "!"; ad.account_reg2_num = 0; auth_data.push_back(ad); @@ -852,51 +828,30 @@ int mmo_auth_new(struct mmo_account *account, char sex, const char *email) static int mmo_auth(struct mmo_account *account, int fd) { - int len, newaccount = 0; -#ifdef PASSWDENC - char md5str[64], md5bin[32]; -#endif + char new_account_sex = '\0'; - const char *ip = ip2str(session[fd]->client_addr.sin_addr); + IP_String ip = ip2str(session[fd]->client_addr.sin_addr); - len = strlen(account->userid) - 2; // Account creation with _M/_F - if (account->passwdenc == 0 && account->userid[len] == '_' && - (account->userid[len + 1] == 'F' || account->userid[len + 1] == 'M') + if (account->passwdenc == 0 + && (account->userid.endswith("_F") || account->userid.endswith("_M")) && new_account_flag == 1 && account_id_count <= END_ACCOUNT_NUM - && len >= 4 && strlen(account->passwd) >= 4) + && (account->userid.size() - 2) >= 4 && account->passwd.size() >= 4) { - if (new_account_flag == 1) - newaccount = 1; - account->userid[len] = '\0'; + new_account_sex = account->userid.back(); + account->userid = stringish(account->userid.orslice_h(2)); } // Strict account search - AuthData *ad = nullptr; - for (AuthData& adi : auth_data) - { - if (strcmp(account->userid, adi.userid) == 0) - { - ad = &adi; - break; - } - } - // if there is no creation request and strict account search fails, we do a no sensitive case research for index - if (newaccount == 0 && !ad) - { - ad = search_account(account->userid); - if (ad) - strzcpy(account->userid, ad->userid, 24); - // for the possible tests/checks afterwards (copy correcte sensitive case). - } + AuthData *ad = search_account(account->userid); if (ad) { int encpasswdok = 0; - if (newaccount) + if (new_account_sex) { LOGIN_LOG("Attempt of creation of an already existant account (account: %s_%c, ip: %s)\n", - account->userid, account->userid[len + 1], ip); + account->userid, new_account_sex, ip); return 9; // 9 = Account already exists } if ((!pass_ok(account->passwd, ad->pass)) && !encpasswdok) @@ -965,7 +920,7 @@ int mmo_auth(struct mmo_account *account, int fd) } else { - if (newaccount == 0) + if (new_account_sex == '\0') { LOGIN_LOG("Unknown account (account: %s, ip: %s)\n", account->userid, ip); @@ -973,11 +928,10 @@ int mmo_auth(struct mmo_account *account, int fd) } else { - int new_id = - mmo_auth_new(account, account->userid[len + 1], "a@a.com"); + int new_id = mmo_auth_new(account, new_account_sex, DEFAULT_EMAIL); LOGIN_LOG("Account creation and authentification accepted (account %s (id: %d), sex: %c, connection with _F/_M, ip: %s)\n", account->userid, new_id, - account->userid[len + 1], ip); + new_account_sex, ip); ad = &auth_data.back(); } } @@ -988,10 +942,10 @@ int mmo_auth(struct mmo_account *account, int fd) account->account_id = ad->account_id; account->login_id1 = random_::generate(); account->login_id2 = random_::generate(); - strzcpy(account->lastlogin, ad->lastlogin, 24); - strzcpy(ad->lastlogin, tmpstr, 24); + account->lastlogin = ad->lastlogin; + ad->lastlogin = tmpstr; account->sex = ad->sex; - strzcpy(ad->last_ip, ip, 16); + ad->last_ip = ip; ad->logincount++; return -1; // account OK @@ -1029,7 +983,7 @@ void char_anti_freeze_system(TimerData *, tick_t) static void parse_fromchar(int fd) { - const char *ip = ip2str(session[fd]->client_addr.sin_addr); + IP_String ip = ip2str(session[fd]->client_addr.sin_addr); int id; for (id = 0; id < MAX_SERVERS; id++) @@ -1144,15 +1098,12 @@ void parse_fromchar(int fd) // we receive a e-mail creation of an account with a default e-mail (no answer) case 0x2715: - { - int acc; - char email[40]; if (RFIFOREST(fd) < 46) return; - acc = RFIFOL(fd, 2); // speed up - RFIFO_STRING(fd, 6, email, 40); - remove_control_chars(email); - if (e_mail_check(email) == 0) + { + int acc = RFIFOL(fd, 2); + AccountEmail email = stringish(RFIFO_STRING<40>(fd, 6)); + if (!e_mail_check(email)) LOGIN_LOG("Char-server '%s': Attempt to create an e-mail on an account with a default e-mail REFUSED - e-mail is invalid (account: %d, ip: %s)\n", server[id].name, acc, ip); else @@ -1160,10 +1111,9 @@ void parse_fromchar(int fd) for (AuthData& ad : auth_data) { if (ad.account_id == acc - && (strcmp(ad.email, "a@a.com") == 0 - || ad.email[0] == '\0')) + && (ad.email == DEFAULT_EMAIL || !ad.email)) { - strzcpy(ad.email, email, 40); + ad.email = email; LOGIN_LOG("Char-server '%s': Create an e-mail on an account with a default e-mail (account: %d, new e-mail: %s, ip: %s).\n", server[id].name, acc, email, ip); goto x2715_out; @@ -1181,15 +1131,17 @@ void parse_fromchar(int fd) case 0x2716: if (RFIFOREST(fd) < 6) return; + { + int account_id = RFIFOL(fd, 2); //PRINTF("parse_fromchar: E-mail/limited time request from '%s' server (concerned account: %d)\n", server[id].name, RFIFOL(fd,2)); for (const AuthData& ad : auth_data) { - if (ad.account_id == RFIFOL(fd, 2)) + if (ad.account_id == account_id) { LOGIN_LOG("Char-server '%s': e-mail of the account %d found (ip: %s).\n", - server[id].name, RFIFOL(fd, 2), ip); + server[id].name, account_id, ip); WFIFOW(fd, 0) = 0x2717; - WFIFOL(fd, 2) = RFIFOL(fd, 2); + WFIFOL(fd, 2) = account_id; WFIFO_STRING(fd, 6, ad.email, 40); WFIFOL(fd, 46) = static_cast(ad.connect_until_time); WFIFOSET(fd, 50); @@ -1197,7 +1149,8 @@ void parse_fromchar(int fd) } } LOGIN_LOG("Char-server '%s': e-mail of the account %d NOT found (ip: %s).\n", - server[id].name, RFIFOL(fd, 2), ip); + server[id].name, account_id, ip); + } x2716_end: RFIFOSKIP(fd, 6); break; @@ -1215,10 +1168,9 @@ void parse_fromchar(int fd) WBUFL(buf, 2) = acc; WBUFL(buf, 6) = 0; size_t len = RFIFOW(fd, 2) - 8; - char pass[len]; - RFIFO_STRING(fd, 8, pass, len); + FString pass = RFIFO_STRING(fd, 8, len); - if (strcmp(pass, gm_pass) == 0) + if (pass == gm_pass) { // only non-GM can become GM if (isGM(acc) == 0) @@ -1227,9 +1179,7 @@ void parse_fromchar(int fd) if (level_new_gm > 0) { // if we can open the file to add the new GM - if ((fp = - fopen_(GM_account_filename, - "a")) != NULL) + if ((fp = fopen(GM_account_filename.c_str(), "a")) != NULL) { timestamp_seconds_buffer tmpstr; stamp_time(tmpstr); @@ -1237,7 +1187,7 @@ void parse_fromchar(int fd) "\n// %s: @GM command on account %d\n%d %d\n", tmpstr, acc, acc, level_new_gm); - fclose_(fp); + fclose(fp); WBUFL(buf, 6) = level_new_gm; read_gm_account(); send_GM_accounts(); @@ -1288,20 +1238,16 @@ void parse_fromchar(int fd) if (RFIFOREST(fd) < 86) return; { - int acc; - char actual_email[40], new_email[40]; - acc = RFIFOL(fd, 2); - RFIFO_STRING(fd, 6, actual_email, 40); - remove_control_chars(actual_email); - RFIFO_STRING(fd, 46, new_email, 40); - remove_control_chars(new_email); - if (e_mail_check(actual_email) == 0) + int acc = RFIFOL(fd, 2); + AccountEmail actual_email = stringish(RFIFO_STRING<40>(fd, 6).to_print()); + AccountEmail new_email = stringish(RFIFO_STRING<40>(fd, 46)); + if (!e_mail_check(actual_email)) LOGIN_LOG("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but actual email is invalid (account: %d, ip: %s)\n", server[id].name, acc, ip); - else if (e_mail_check(new_email) == 0) + else if (!e_mail_check(new_email)) LOGIN_LOG("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command) with a invalid new e-mail (account: %d, ip: %s)\n", server[id].name, acc, ip); - else if (strcasecmp(new_email, "a@a.com") == 0) + else if (new_email == DEFAULT_EMAIL) LOGIN_LOG("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command) with a default e-mail (account: %d, ip: %s)\n", server[id].name, acc, ip); else @@ -1310,9 +1256,9 @@ void parse_fromchar(int fd) { if (ad.account_id == acc) { - if (strcasecmp(ad.email, actual_email) == 0) + if (ad.email == actual_email) { - strzcpy(ad.email, new_email, 40); + ad.email = new_email; LOGIN_LOG("Char-server '%s': Modify an e-mail on an account (@email GM command) (account: %d (%s), new e-mail: %s, ip: %s).\n", server[id].name, acc, ad.userid, new_email, ip); @@ -1396,12 +1342,14 @@ void parse_fromchar(int fd) else timestamp = ad.ban_until_time; struct tm tmtime = timestamp; - tmtime.tm_year += static_cast(RFIFOW(fd, 6)); - tmtime.tm_mon += static_cast(RFIFOW(fd, 8)); - tmtime.tm_mday += static_cast(RFIFOW(fd, 10)); - tmtime.tm_hour += static_cast(RFIFOW(fd, 12)); - tmtime.tm_min += static_cast(RFIFOW(fd, 14)); - tmtime.tm_sec += static_cast(RFIFOW(fd, 16)); + HumanTimeDiff ban_diff; + RFIFO_STRUCT(fd, 6, ban_diff); + tmtime.tm_year += ban_diff.year; + tmtime.tm_mon += ban_diff.month; + tmtime.tm_mday += ban_diff.day; + tmtime.tm_hour += ban_diff.hour; + tmtime.tm_min += ban_diff.minute; + tmtime.tm_sec += ban_diff.second; timestamp = tmtime; if (timestamp.okay()) { @@ -1521,8 +1469,7 @@ void parse_fromchar(int fd) p < len && j < ACCOUNT_REG2_NUM; p += 36, j++) { - RFIFO_STRING(fd, p, ad.account_reg2[j].str, 32); - remove_control_chars(ad.account_reg2[j].str); + ad.account_reg2[j].str = stringish(RFIFO_STRING<32>(fd, p).to_print()); ad.account_reg2[j].value = RFIFOL(fd, p + 32); } ad.account_reg2_num = j; @@ -1577,13 +1524,9 @@ void parse_fromchar(int fd) if (RFIFOREST(fd) < 54) return; { - int acc; - char actual_pass[24], new_pass[24]; - acc = RFIFOL(fd, 2); - RFIFO_STRING(fd, 6, actual_pass, 24); - remove_control_chars(actual_pass); - RFIFO_STRING(fd, 30, new_pass, 24); - remove_control_chars(new_pass); + int acc = RFIFOL(fd, 2); + AccountPass actual_pass = stringish(RFIFO_STRING<24>(fd, 6).to_print()); + AccountPass new_pass = stringish(RFIFO_STRING<24>(fd, 30).to_print()); int status = 0; @@ -1593,12 +1536,12 @@ void parse_fromchar(int fd) { if (pass_ok(actual_pass, ad.pass)) { - if (strlen(new_pass) < 4) + if (new_pass.size() < 4) status = 3; else { status = 1; - strcpy(ad.pass, MD5_saltcrypt(new_pass, make_salt())); + ad.pass = MD5_saltcrypt(new_pass, make_salt()); LOGIN_LOG("Char-server '%s': Change pass success (account: %d (%s), ip: %s.\n", server[id].name, acc, ad.userid, ip); @@ -1626,8 +1569,7 @@ void parse_fromchar(int fd) default: { - FILE *logfp; - logfp = fopen_(login_log_unknown_packets_filename, "a"); + FILE *logfp = fopen(login_log_unknown_packets_filename.c_str(), "a"); if (logfp) { timestamp_milliseconds_buffer timestr; @@ -1657,7 +1599,7 @@ void parse_fromchar(int fd) else if ((i + 1) % 16 == 0) { FPRINTF(logfp, " %s\n", tmpstr); - strzcpy(tmpstr, "", 16 + 1); + std::fill(tmpstr + 0, tmpstr + 17, '\0'); } } if (i % 16 != 0) @@ -1671,7 +1613,7 @@ void parse_fromchar(int fd) FPRINTF(logfp, " %s\n", tmpstr); } FPRINTF(logfp, "\n"); - fclose_(logfp); + fclose(logfp); } } PRINTF("parse_fromchar: Unknown packet 0x%x (from a char-server)! -> disconnection.\n", @@ -1690,9 +1632,7 @@ void parse_fromchar(int fd) static void parse_admin(int fd) { - char account_name[24]; - - const char *ip = ip2str(session[fd]->client_addr.sin_addr); + IP_String ip = ip2str(session[fd]->client_addr.sin_addr); if (session[fd]->eof) { @@ -1792,14 +1732,14 @@ void parse_admin(int fd) return; { struct mmo_account ma; - RFIFO_STRING(fd, 2, ma.userid, 24); - RFIFO_STRING(fd, 26, ma.passwd, 24); - strzcpy(ma.lastlogin, "-", 24); + ma.userid = stringish(RFIFO_STRING<24>(fd, 2).to_print()); + ma.passwd = stringish(RFIFO_STRING<24>(fd, 26).to_print()); + ma.lastlogin = stringish("-"); ma.sex = RFIFOB(fd, 50); WFIFOW(fd, 0) = 0x7931; WFIFOL(fd, 2) = -1; WFIFO_STRING(fd, 6, ma.userid, 24); - if (strlen(ma.userid) < 4 || strlen(ma.passwd) < 4) + if (ma.userid.size() < 4 || ma.passwd.size() < 4) { LOGIN_LOG("'ladmin': Attempt to create an invalid account (account or pass is too short, ip: %s)\n", ip); @@ -1816,11 +1756,9 @@ void parse_admin(int fd) } else { - remove_control_chars(ma.userid); - remove_control_chars(ma.passwd); for (const AuthData& ad : auth_data) { - if (strncmp(ad.userid, ma.userid, 24) == 0) + if (ad.userid == ma.userid) { LOGIN_LOG("'ladmin': Attempt to create an already existing account (account: %s ip: %s)\n", ad.userid, ip); @@ -1828,11 +1766,8 @@ void parse_admin(int fd) } } { - int new_id; - char email[40]; - RFIFO_STRING(fd, 51, email, 40); - remove_control_chars(email); - new_id = mmo_auth_new(&ma, ma.sex, email); + AccountEmail email = stringish(RFIFO_STRING<40>(fd, 51)); + int new_id = mmo_auth_new(&ma, ma.sex, email); LOGIN_LOG("'ladmin': Account creation (account: %s (id: %d), sex: %c, email: %s, ip: %s)\n", ma.userid, new_id, ma.sex, auth_data.back().email, ip); @@ -1851,12 +1786,10 @@ void parse_admin(int fd) { WFIFOW(fd, 0) = 0x7933; WFIFOL(fd, 2) = -1; - RFIFO_STRING(fd, 2, account_name, 24); - remove_control_chars(account_name); + AccountName account_name = stringish(RFIFO_STRING<24>(fd, 2).to_print()); AuthData *ad = search_account(account_name); if (ad) { - // TODO rename so I don't need anti-shadow braces // Char-server is notified of deletion (for characters deletion). uint8_t buf[6]; WBUFW(buf, 0) = 0x2730; @@ -1870,11 +1803,11 @@ void parse_admin(int fd) ad->userid, ad->account_id, ip); { - std::string buf2 = mmo_auth_tostr(ad); + FString buf2 = mmo_auth_tostr(ad); LOGIN_LOG("%s\n", buf2); } // delete account - strzcpy(ad->userid, "", 24); + ad->userid = AccountName(); ad->account_id = -1; } else @@ -1894,15 +1827,13 @@ void parse_admin(int fd) { WFIFOW(fd, 0) = 0x7935; WFIFOL(fd, 2) = -1; - RFIFO_STRING(fd, 2, account_name, 24); - remove_control_chars(account_name); + AccountName account_name = stringish(RFIFO_STRING<24>(fd, 2).to_print()); AuthData *ad = search_account(account_name); if (ad) { WFIFO_STRING(fd, 6, ad->userid, 24); - char plain[24]; - RFIFO_STRING(fd, 26, plain, 24); - strzcpy(ad->pass, MD5_saltcrypt(plain, make_salt()), 40); + AccountPass plain = stringish(RFIFO_STRING<24>(fd, 26)); + ad->pass = MD5_saltcrypt(plain, make_salt()); WFIFOL(fd, 2) = ad->account_id; LOGIN_LOG("'ladmin': Modification of a password (account: %s, new password: %s, ip: %s)\n", ad->userid, ad->pass, ip); @@ -1922,18 +1853,15 @@ void parse_admin(int fd) if (RFIFOREST(fd) < 50) return; { - char error_message[20]; - int statut; WFIFOW(fd, 0) = 0x7937; WFIFOL(fd, 2) = -1; - RFIFO_STRING(fd, 2, account_name, 24); - remove_control_chars(account_name); - statut = RFIFOL(fd, 26); - RFIFO_STRING(fd, 30, error_message, 20); - remove_control_chars(error_message); - if (statut != 7 || error_message[0] == '\0') - { // 7: // 6 = Your are Prohibited to log in until %s - strcpy(error_message, "-"); + AccountName account_name = stringish(RFIFO_STRING<24>(fd, 2).to_print()); + int statut = RFIFOL(fd, 26); + timestamp_seconds_buffer error_message = stringish(RFIFO_STRING<20>(fd, 30).to_print()); + if (statut != 7 || !error_message) + { + // 7: // 6 = Your are Prohibited to log in until %s + error_message = stringish("-"); } AuthData *ad = search_account(account_name); if (ad) @@ -1941,7 +1869,7 @@ void parse_admin(int fd) WFIFO_STRING(fd, 6, ad->userid, 24); WFIFOL(fd, 2) = ad->account_id; if (ad->state == statut - && strcmp(ad->error_message, error_message) == 0) + && ad->error_message == error_message) LOGIN_LOG("'ladmin': Modification of a state, but the state of the account is already the good state (account: %s, received state: %d, ip: %s)\n", account_name, statut, ip); else @@ -1967,7 +1895,7 @@ void parse_admin(int fd) auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) } ad->state = statut; - strzcpy(ad->error_message, error_message, 20); + ad->error_message = error_message; } } else @@ -2010,14 +1938,12 @@ void parse_admin(int fd) { WFIFOW(fd, 0) = 0x793b; WFIFOL(fd, 2) = -1; - RFIFO_STRING(fd, 2, account_name, 24); - remove_control_chars(account_name); + AccountName account_name = stringish(RFIFO_STRING<24>(fd, 2).to_print()); const AuthData *ad = search_account(account_name); if (ad) { WFIFO_STRING(fd, 6, ad->userid, 24); - char pass[24]; - RFIFO_STRING(fd, 26, pass, 24); + AccountPass pass = stringish(RFIFO_STRING<24>(fd, 26)); if (pass_ok(pass, ad->pass)) { WFIFOL(fd, 2) = ad->account_id; @@ -2027,9 +1953,8 @@ void parse_admin(int fd) } else { - remove_control_chars(pass); LOGIN_LOG("'ladmin': Failure of password check (account: %s, proposed pass: %s, ip: %s)\n", - ad->userid, pass, ip); + ad->userid, pass.to_print(), ip); } } else @@ -2046,10 +1971,10 @@ void parse_admin(int fd) case 0x793c: // Request to modify sex if (RFIFOREST(fd) < 27) return; + { WFIFOW(fd, 0) = 0x793d; WFIFOL(fd, 2) = -1; - RFIFO_STRING(fd, 2, account_name, 24); - remove_control_chars(account_name); + AccountName account_name = stringish(RFIFO_STRING<24>(fd, 2).to_print()); WFIFO_STRING(fd, 6, account_name, 24); { char sex; @@ -2103,16 +2028,17 @@ void parse_admin(int fd) } } WFIFOSET(fd, 30); + } RFIFOSKIP(fd, 27); break; case 0x793e: // Request to modify GM level if (RFIFOREST(fd) < 27) return; + { WFIFOW(fd, 0) = 0x793f; WFIFOL(fd, 2) = -1; - RFIFO_STRING(fd, 2, account_name, 24); - remove_control_chars(account_name); + AccountName account_name = stringish(RFIFO_STRING<24>(fd, 2).to_print()); WFIFO_STRING(fd, 6, account_name, 24); { char new_gm_level; @@ -2137,13 +2063,9 @@ void parse_admin(int fd) char line[512]; int GM_account, GM_level; int modify_flag; - if ((fp2 = - lock_fopen(GM_account_filename, - &lock)) != NULL) + if ((fp2 = lock_fopen(GM_account_filename, &lock)) != NULL) { - if ((fp = - fopen_(GM_account_filename, - "r")) != NULL) + if ((fp = fopen(GM_account_filename.c_str(), "r")) != NULL) { timestamp_seconds_buffer tmpstr; stamp_time(tmpstr); @@ -2152,12 +2074,8 @@ void parse_admin(int fd) while (fgets(line, sizeof(line) - 1, fp)) { while (line[0] != '\0' - && (line[strlen(line) - 1] - == '\n' - || line[strlen(line) - - 1] == '\r')) - line[strlen(line) - 1] = - '\0'; + && line[strlen(line) - 1] == '\n') + line[strlen(line) - 1] = '\0'; if ((line[0] == '/' && line[1] == '/') || line[0] == '\0') @@ -2209,7 +2127,7 @@ void parse_admin(int fd) tmpstr, acc, ad->userid, acc, new_gm_level); - fclose_(fp); + fclose(fp); } else { @@ -2249,33 +2167,32 @@ void parse_admin(int fd) } } WFIFOSET(fd, 30); + } RFIFOSKIP(fd, 27); break; case 0x7940: // Request to modify e-mail if (RFIFOREST(fd) < 66) return; + { WFIFOW(fd, 0) = 0x7941; WFIFOL(fd, 2) = -1; - RFIFO_STRING(fd, 2, account_name, 24); - remove_control_chars(account_name); + AccountName account_name = stringish(RFIFO_STRING<24>(fd, 2).to_print()); WFIFO_STRING(fd, 6, account_name, 24); { - char email[40]; - RFIFO_STRING(fd, 26, email, 40); - if (e_mail_check(email) == 0) + AccountEmail email = stringish(RFIFO_STRING<40>(fd, 26)); + if (!e_mail_check(email)) { LOGIN_LOG("'ladmin': Attempt to give an invalid e-mail (account: %s, ip: %s)\n", account_name, ip); } else { - remove_control_chars(email); AuthData *ad = search_account(account_name); if (ad) { WFIFO_STRING(fd, 6, ad->userid, 24); - strzcpy(ad->email, email, 40); + ad->email = email; WFIFOL(fd, 2) = ad->account_id; LOGIN_LOG("'ladmin': Modification of an email (account: %s, new e-mail: %s, ip: %s)\n", ad->userid, email, ip); @@ -2288,6 +2205,7 @@ void parse_admin(int fd) } } WFIFOSET(fd, 30); + } RFIFOSKIP(fd, 66); break; @@ -2298,27 +2216,23 @@ void parse_admin(int fd) { WFIFOW(fd, 0) = 0x7943; WFIFOL(fd, 2) = -1; - RFIFO_STRING(fd, 2, account_name, 24); - remove_control_chars(account_name); + AccountName account_name = stringish(RFIFO_STRING<24>(fd, 2).to_print()); AuthData *ad = search_account(account_name); if (ad) { - size_t size_of_memo = sizeof(ad->memo); WFIFO_STRING(fd, 6, ad->userid, 24); - strzcpy(ad->memo, "", size_of_memo); + ad->memo = ""; if (RFIFOW(fd, 26) == 0) { - strzcpy(ad->memo, "!", size_of_memo); + ad->memo = "!"; } else { size_t len = RFIFOW(fd, 26); - if (len > size_of_memo) - len = size_of_memo; - RFIFO_STRING(fd, 28, ad->memo, len); + // may truncate + ad->memo = RFIFO_STRING(fd, 28, len); } - ad->memo[size_of_memo - 1] = '\0'; - remove_control_chars(ad->memo); + ad->memo = ad->memo.to_print(); WFIFOL(fd, 2) = ad->account_id; LOGIN_LOG("'ladmin': Modification of a memo field (account: %s, new memo: %s, ip: %s)\n", ad->userid, ad->memo, ip); @@ -2340,8 +2254,7 @@ void parse_admin(int fd) { WFIFOW(fd, 0) = 0x7945; WFIFOL(fd, 2) = -1; - RFIFO_STRING(fd, 2, account_name, 24); - remove_control_chars(account_name); + AccountName account_name = stringish(RFIFO_STRING<24>(fd, 2).to_print()); const AuthData *ad = search_account(account_name); if (ad) { @@ -2365,24 +2278,27 @@ void parse_admin(int fd) case 0x7946: // Request to found an account name if (RFIFOREST(fd) < 6) return; + { + int account_id = RFIFOL(fd, 2); WFIFOW(fd, 0) = 0x7947; - WFIFOL(fd, 2) = RFIFOL(fd, 2); + WFIFOL(fd, 2) = account_id; WFIFO_ZERO(fd, 6, 24); for (const AuthData& ad : auth_data) { - if (ad.account_id == RFIFOL(fd, 2)) + if (ad.account_id == account_id) { WFIFO_STRING(fd, 6, ad.userid, 24); LOGIN_LOG("'ladmin': Request (by id) of an account name (account: %s, id: %d, ip: %s)\n", - ad.userid, RFIFOL(fd, 2), ip); + ad.userid, account_id, ip); goto x7946_out; } } LOGIN_LOG("'ladmin': Name request (by id) of an unknown account (id: %d, ip: %s)\n", - RFIFOL(fd, 2), ip); + account_id, ip); WFIFO_STRING(fd, 6, "", 24); x7946_out: WFIFOSET(fd, 30); + } RFIFOSKIP(fd, 6); break; @@ -2392,10 +2308,9 @@ void parse_admin(int fd) { WFIFOW(fd, 0) = 0x7949; WFIFOL(fd, 2) = -1; - RFIFO_STRING(fd, 2, account_name, 24); - remove_control_chars(account_name); + AccountName account_name = stringish(RFIFO_STRING<24>(fd, 2).to_print()); TimeT timestamp = static_cast(RFIFOL(fd, 26)); - timestamp_seconds_buffer tmpstr = "unlimited"; + timestamp_seconds_buffer tmpstr = stringish("unlimited"); if (timestamp) stamp_time(tmpstr, ×tamp); AuthData *ad = search_account(account_name); @@ -2431,12 +2346,11 @@ void parse_admin(int fd) { WFIFOW(fd, 0) = 0x794b; WFIFOL(fd, 2) = -1; - RFIFO_STRING(fd, 2, account_name, 24); - remove_control_chars(account_name); + AccountName account_name = stringish(RFIFO_STRING<24>(fd, 2).to_print()); TimeT timestamp = static_cast(RFIFOL(fd, 26)); if (timestamp <= TimeT::now()) timestamp = TimeT(); - timestamp_seconds_buffer tmpstr = "no banishment"; + timestamp_seconds_buffer tmpstr = stringish("no banishment"); if (timestamp) stamp_time(tmpstr, ×tamp); AuthData *ad = search_account(account_name); @@ -2486,8 +2400,7 @@ void parse_admin(int fd) { WFIFOW(fd, 0) = 0x794d; WFIFOL(fd, 2) = -1; - RFIFO_STRING(fd, 2, account_name, 24); - remove_control_chars(account_name); + AccountName account_name = stringish(RFIFO_STRING<24>(fd, 2).to_print()); AuthData *ad = search_account(account_name); if (ad) { @@ -2501,25 +2414,27 @@ void parse_admin(int fd) else timestamp = ad->ban_until_time; struct tm tmtime = timestamp; - tmtime.tm_year += static_cast(RFIFOW(fd, 26)); - tmtime.tm_mon += static_cast(RFIFOW(fd, 28)); - tmtime.tm_mday += static_cast(RFIFOW(fd, 30)); - tmtime.tm_hour += static_cast(RFIFOW(fd, 32)); - tmtime.tm_min += static_cast(RFIFOW(fd, 34)); - tmtime.tm_sec += static_cast(RFIFOW(fd, 36)); + HumanTimeDiff ban_diff; + RFIFO_STRUCT(fd, 26, ban_diff); + tmtime.tm_year += ban_diff.year; + tmtime.tm_mon += ban_diff.month; + tmtime.tm_mday += ban_diff.day; + tmtime.tm_hour += ban_diff.hour; + tmtime.tm_min += ban_diff.minute; + tmtime.tm_sec += ban_diff.second; timestamp = tmtime; if (timestamp.okay()) { if (timestamp <= now) timestamp = TimeT(); - timestamp_seconds_buffer tmpstr = "no banishment"; + timestamp_seconds_buffer tmpstr = stringish("no banishment"); if (timestamp) stamp_time(tmpstr, ×tamp); LOGIN_LOG("'ladmin': Adjustment of a final date of a banishment (account: %s, (%+d y %+d m %+d d %+d h %+d mn %+d s) -> new validity: %lld (%s), ip: %s)\n", ad->userid, - static_cast(RFIFOW(fd, 26)), static_cast(RFIFOW(fd, 28)), - static_cast(RFIFOW(fd, 30)), static_cast(RFIFOW(fd, 32)), - static_cast(RFIFOW(fd, 34)), static_cast(RFIFOW(fd, 36)), + ban_diff.year, ban_diff.month, + ban_diff.day, ban_diff.hour, + ban_diff.minute, ban_diff.second, timestamp, tmpstr, ip); @@ -2543,16 +2458,16 @@ void parse_admin(int fd) } else { - timestamp_seconds_buffer tmpstr = "no banishment"; + timestamp_seconds_buffer tmpstr = stringish("no banishment"); if (ad->ban_until_time) stamp_time(tmpstr, &ad->ban_until_time); LOGIN_LOG("'ladmin': Impossible to adjust the final date of a banishment (account: %s, %lld (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> ???, ip: %s)\n", ad->userid, ad->ban_until_time, tmpstr, - static_cast(RFIFOW(fd, 26)), static_cast(RFIFOW(fd, 28)), - static_cast(RFIFOW(fd, 30)), static_cast(RFIFOW(fd, 32)), - static_cast(RFIFOW(fd, 34)), static_cast(RFIFOW(fd, 36)), + ban_diff.year, ban_diff.month, + ban_diff.day, ban_diff.hour, + ban_diff.minute, ban_diff.second, ip); } WFIFOL(fd, 30) = static_cast(ad->ban_until_time); @@ -2595,24 +2510,15 @@ void parse_admin(int fd) WFIFOW(fd, 2) = 0; size_t len = RFIFOL(fd, 4); - char message[len]; - RFIFO_STRING(fd, 8, message, len); - remove_control_chars(message); - const char *message_ptr = message; + FString message = RFIFO_STRING(fd, 8, len).to_print(); LOGIN_LOG("'ladmin': Receiving a message for broadcast (message: %s, ip: %s)\n", - message_ptr, ip); + message, ip); // send same message to all char-servers (no answer) uint8_t buf[len + 8]; RFIFO_BUF_CLONE(fd, buf, 8 + len); WBUFW(buf, 0) = 0x2726; charif_sendallwos(-1, buf, 8 + len); -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wuninitialized" // work around a gcc bug -#pragma GCC diagnostic ignored "-Wunknown-pragmas" -#pragma GCC diagnostic ignored "-Wpragmas" -#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" } -#pragma GCC diagnostic pop } x794e_have_no_server: WFIFOSET(fd, 4); @@ -2625,8 +2531,7 @@ void parse_admin(int fd) { WFIFOW(fd, 0) = 0x7951; WFIFOL(fd, 2) = -1; - RFIFO_STRING(fd, 2, account_name, 24); - remove_control_chars(account_name); + AccountName account_name = stringish(RFIFO_STRING<24>(fd, 2).to_print()); AuthData *ad = search_account(account_name); if (ad) { @@ -2645,17 +2550,19 @@ void parse_admin(int fd) if (!timestamp || timestamp < now) timestamp = now; struct tm tmtime = timestamp; - tmtime.tm_year += static_cast(RFIFOW(fd, 26)); - tmtime.tm_mon += static_cast(RFIFOW(fd, 28)); - tmtime.tm_mday += static_cast(RFIFOW(fd, 30)); - tmtime.tm_hour += static_cast(RFIFOW(fd, 32)); - tmtime.tm_min += static_cast(RFIFOW(fd, 34)); - tmtime.tm_sec += static_cast(RFIFOW(fd, 36)); + HumanTimeDiff v_diff; + RFIFO_STRUCT(fd, 26, v_diff); + tmtime.tm_year += v_diff.year; + tmtime.tm_mon += v_diff.month; + tmtime.tm_mday += v_diff.day; + tmtime.tm_hour += v_diff.hour; + tmtime.tm_min += v_diff.minute; + tmtime.tm_sec += v_diff.second; timestamp = tmtime; if (timestamp.okay()) { - timestamp_seconds_buffer tmpstr = "unlimited"; - timestamp_seconds_buffer tmpstr2 = "unlimited"; + timestamp_seconds_buffer tmpstr = stringish("unlimited"); + timestamp_seconds_buffer tmpstr2 = stringish("unlimited"); if (ad->connect_until_time) stamp_time(tmpstr, &ad->connect_until_time); if (timestamp) @@ -2664,12 +2571,12 @@ void parse_admin(int fd) ad->userid, ad->connect_until_time, tmpstr, - static_cast(RFIFOW(fd, 26)), - static_cast(RFIFOW(fd, 28)), - static_cast(RFIFOW(fd, 30)), - static_cast(RFIFOW(fd, 32)), - static_cast(RFIFOW(fd, 34)), - static_cast(RFIFOW(fd, 36)), + v_diff.year, + v_diff.month, + v_diff.day, + v_diff.hour, + v_diff.minute, + v_diff.second, timestamp, tmpstr2, ip); @@ -2678,19 +2585,19 @@ void parse_admin(int fd) } else { - timestamp_seconds_buffer tmpstr = "unlimited"; + timestamp_seconds_buffer tmpstr = stringish("unlimited"); if (ad->connect_until_time) stamp_time(tmpstr, &ad->connect_until_time); LOGIN_LOG("'ladmin': Impossible to adjust a validity limit (account: %s, %lld (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> ???, ip: %s)\n", ad->userid, ad->connect_until_time, tmpstr, - static_cast(RFIFOW(fd, 26)), - static_cast(RFIFOW(fd, 28)), - static_cast(RFIFOW(fd, 30)), - static_cast(RFIFOW(fd, 32)), - static_cast(RFIFOW(fd, 34)), - static_cast(RFIFOW(fd, 36)), + v_diff.year, + v_diff.month, + v_diff.day, + v_diff.hour, + v_diff.minute, + v_diff.second, ip); WFIFOL(fd, 30) = 0; } @@ -2714,8 +2621,7 @@ void parse_admin(int fd) { WFIFOW(fd, 0) = 0x7953; WFIFOL(fd, 2) = -1; - RFIFO_STRING(fd, 2, account_name, 24); - remove_control_chars(account_name); + AccountName account_name = stringish(RFIFO_STRING<24>(fd, 2).to_print()); const AuthData *ad = search_account(account_name); if (ad) { @@ -2731,7 +2637,7 @@ void parse_admin(int fd) WFIFO_STRING(fd, 100, ad->email, 40); WFIFOL(fd, 140) = static_cast(ad->connect_until_time); WFIFOL(fd, 144) = static_cast(ad->ban_until_time); - size_t len = strlen(ad->memo) + 1; + size_t len = ad->memo.size() + 1; WFIFOW(fd, 148) = len; WFIFO_STRING(fd, 150, ad->memo, len); LOGIN_LOG("'ladmin': Sending information of an account (request by the name; account: %s, id: %d, ip: %s)\n", @@ -2774,7 +2680,7 @@ void parse_admin(int fd) WFIFO_STRING(fd, 100, ad.email, 40); WFIFOL(fd, 140) = static_cast(ad.connect_until_time); WFIFOL(fd, 144) = static_cast(ad.ban_until_time); - size_t len = strlen(ad.memo) + 1; + size_t len = ad.memo.size() + 1; WFIFOW(fd, 148) = len; WFIFO_STRING(fd, 150, ad.memo, len); WFIFOSET(fd, 150 + len); @@ -2803,8 +2709,7 @@ void parse_admin(int fd) default: { - FILE *logfp; - logfp = fopen_(login_log_unknown_packets_filename, "a"); + FILE *logfp = fopen(login_log_unknown_packets_filename.c_str(), "a"); if (logfp) { timestamp_milliseconds_buffer timestr; @@ -2834,7 +2739,7 @@ void parse_admin(int fd) else if ((i + 1) % 16 == 0) { FPRINTF(logfp, " %s\n", tmpstr); - strzcpy(tmpstr, "", 16 + 1); + std::fill(tmpstr + 0, tmpstr + 17, '\0'); } } if (i % 16 != 0) @@ -2848,7 +2753,7 @@ void parse_admin(int fd) FPRINTF(logfp, " %s\n", tmpstr); } FPRINTF(logfp, "\n"); - fclose_(logfp); + fclose(logfp); } } LOGIN_LOG("'ladmin': End of connection, unknown packet (ip: %s)\n", @@ -2900,7 +2805,7 @@ void parse_login(int fd) int result, j; uint8_t *p = reinterpret_cast(&session[fd]->client_addr.sin_addr); - const char *ip = ip2str(session[fd]->client_addr.sin_addr); + IP_String ip = ip2str(session[fd]->client_addr.sin_addr); if (session[fd]->eof) { @@ -2916,8 +2821,7 @@ void parse_login(int fd) { if (RFIFOREST(fd) >= ((RFIFOW(fd, 0) == 0x64) ? 55 : 47)) { - char account_name[24]; - RFIFO_STRING(fd, 6, account_name, 24); + AccountName account_name = stringish(RFIFO_STRING<24>(fd, 6)); PRINTF("parse_login: connection #%d, packet: 0x%x (with being read: %zu), account: %s.\n", fd, RFIFOW(fd, 0), RFIFOREST(fd), account_name); @@ -2927,8 +2831,7 @@ void parse_login(int fd) { if (RFIFOREST(fd) >= 86) { - char server_name[16]; - RFIFO_STRING(fd, 60, server_name, 16); + ServerName server_name = stringish(RFIFO_STRING<20>(fd, 60)); PRINTF("parse_login: connection #%d, packet: 0x%x (with being read: %zu), server: %s.\n", fd, RFIFOW(fd, 0), RFIFOREST(fd), server_name); @@ -2957,10 +2860,8 @@ void parse_login(int fd) if (RFIFOREST(fd) < 55) return; - RFIFO_STRING(fd, 6, account.userid, 24); - remove_control_chars(account.userid); - RFIFO_STRING(fd, 30, account.passwd, 24); - remove_control_chars(account.passwd); + account.userid = stringish(RFIFO_STRING<24>(fd, 6).to_print()); + account.passwd = stringish(RFIFO_STRING<24>(fd, 30).to_print()); account.passwdenc = 0; LOGIN_LOG("Request for connection (non encryption mode) of %s (ip: %s).\n", @@ -3022,10 +2923,9 @@ void parse_login(int fd) */ // if (version_2 & VERSION_2_UPDATEHOST) { - size_t host_len = strlen(update_host); - if (host_len > 0) + if (update_host) { - host_len++; + size_t host_len = update_host.size() + 1; WFIFOW(fd, 0) = 0x63; WFIFOW(fd, 2) = 4 + host_len; WFIFO_STRING(fd, 4, update_host, host_len); @@ -3041,7 +2941,7 @@ void parse_login(int fd) if (server_fd[i] >= 0) { if (lan_ip_check(p)) - WFIFOL(fd, 47 + server_num * 32) = inet_addr(lan_char_ip); + WFIFOL(fd, 47 + server_num * 32) = inet_addr(lan_char_ip.c_str()); else WFIFOL(fd, 47 + server_num * 32) = server[i].ip; WFIFOW(fd, 47 + server_num * 32 + 4) = server[i].port; @@ -3118,56 +3018,15 @@ void parse_login(int fd) RFIFOSKIP(fd, (RFIFOW(fd, 0) == 0x64) ? 55 : 47); break; - case 0x01db: // Sending request of the coding key - case 0x791a: // Sending request of the coding key (administration packet) - { - if (session[fd]->session_data) - { - PRINTF("login: abnormal request of MD5 key (already opened session).\n"); - session[fd]->eof = 1; - return; - } - std::unique_ptr ld; - ld = make_unique(); - if (RFIFOW(fd, 0) == 0x01db) - { - LOGIN_LOG("Sending request of the coding key (ip: %s)\n", - ip); - } - else - { - LOGIN_LOG("'ladmin': Sending request of the coding key (ip: %s)\n", - ip); - } - // TODO fix or get rid of this - // Creation of the coding key - strzcpy(ld->md5key, "", sizeof(ld->md5key)); - ld->md5keylen = random_::in(12, 15); - for (int i = 0; i < ld->md5keylen; i++) - ld->md5key[i] = random_::in(1, 255); - - RFIFOSKIP(fd, 2); - WFIFOW(fd, 0) = 0x01dc; - WFIFOW(fd, 2) = 4 + ld->md5keylen + 1; - WFIFO_STRING(fd, 4, ld->md5key, ld->md5keylen + 1); - WFIFOSET(fd, WFIFOW(fd, 2)); - session[fd]->session_data = std::move(ld); - } - break; - case 0x2710: // Connection request of a char-server if (RFIFOREST(fd) < 86) return; { int len; - char server_name[20]; - RFIFO_STRING(fd, 2, account.userid, 24); - remove_control_chars(account.userid); - RFIFO_STRING(fd, 26, account.passwd, 24); - remove_control_chars(account.passwd); + account.userid = stringish(RFIFO_STRING<24>(fd, 2).to_print()); + account.passwd = stringish(RFIFO_STRING<24>(fd, 26).to_print()); account.passwdenc = 0; - RFIFO_STRING(fd, 60, server_name, 20); - remove_control_chars(server_name); + ServerName server_name = stringish(RFIFO_STRING<20>(fd, 60).to_print()); LOGIN_LOG("Connection request of the char-server '%s' @ %d.%d.%d.%d:%d (ip: %s)\n", server_name, RFIFOB(fd, 54), RFIFOB(fd, 55), RFIFOB(fd, 56), RFIFOB(fd, 57), RFIFOW(fd, 58), @@ -3178,7 +3037,7 @@ void parse_login(int fd) { // If this is the main server, and we don't already have a main server if (server_fd[0] <= 0 - && strcasecmp(server_name, main_server) == 0) + && server_name == main_server) { account.account_id = 0; } @@ -3208,7 +3067,7 @@ void parse_login(int fd) server[account.account_id] = mmo_char_server{}; server[account.account_id].ip = RFIFOL(fd, 54); server[account.account_id].port = RFIFOW(fd, 58); - strzcpy(server[account.account_id].name, server_name, 20); + server[account.account_id].name = server_name; server[account.account_id].users = 0; server[account.account_id].maintenance = RFIFOW(fd, 82); server[account.account_id].is_new = RFIFOW(fd, 84); @@ -3278,16 +3137,13 @@ void parse_login(int fd) } else { - struct login_session_data *ld = static_cast(session[fd]->session_data.get()); if (RFIFOW(fd, 2) == 0) { // non encrypted password - char password[24]; - RFIFO_STRING(fd, 4, password, 24); - remove_control_chars(password); + AccountPass password = stringish(RFIFO_STRING<24>(fd, 4).to_print()); // If remote administration is enabled and password sent by client matches password read from login server configuration file if ((admin_state == 1) - && (strcmp(password, admin_pass) == 0)) + && (password == admin_pass)) { LOGIN_LOG("'ladmin'-login: Connection in administration mode accepted (non encrypted password: %s, ip: %s)\n", password, ip); @@ -3303,39 +3159,10 @@ void parse_login(int fd) password, ip); } else - { // encrypted password - if (!ld) - PRINTF("'ladmin'-login: error! MD5 key not created/requested for an administration login.\n"); - else + { + // encrypted password { - char md5str[64] = ""; - uint8_t md5bin[32]; - if (RFIFOW(fd, 2) == 1) - { - strcpy(md5str, ld->md5key); // 20 - strcat(md5str, admin_pass); // 24 - } - else if (RFIFOW(fd, 2) == 2) - { - strcpy(md5str, admin_pass); // 24 - strcat(md5str, ld->md5key); // 20 - } - MD5_to_bin(MD5_from_cstring(md5str), md5bin); - // If remote administration is enabled and password hash sent by client matches hash of password read from login server configuration file - if ((admin_state == 1) - && really_memequal(md5bin, static_cast(RFIFOP(fd, 4)), 16)) - { - LOGIN_LOG("'ladmin'-login: Connection in administration mode accepted (encrypted password, ip: %s)\n", - ip); - PRINTF("Connection of a remote administration accepted (encrypted password).\n"); - WFIFOB(fd, 2) = 0; - session[fd]->func_parse = parse_admin; - } - else if (admin_state != 1) - LOGIN_LOG("'ladmin'-login: Connection in administration mode REFUSED - remote administration is disabled (encrypted password, ip: %s)\n", - ip); - else - LOGIN_LOG("'ladmin'-login: Connection in administration mode REFUSED - invalid password (encrypted password, ip: %s)\n", + LOGIN_LOG("'ladmin'-login: Connection in administration mode REFUSED - encrypted login is disabled (ip: %s)\n", ip); } } @@ -3347,8 +3174,7 @@ void parse_login(int fd) default: if (save_unknown_packets) { - FILE *logfp; - logfp = fopen_(login_log_unknown_packets_filename, "a"); + FILE *logfp = fopen(login_log_unknown_packets_filename.c_str(), "a"); if (logfp) { timestamp_milliseconds_buffer timestr; @@ -3381,7 +3207,7 @@ void parse_login(int fd) else if ((i + 1) % 16 == 0) { FPRINTF(logfp, " %s\n", tmpstr); - strzcpy(tmpstr, "", 16 + 1); + std::fill(tmpstr + 0, tmpstr + 17, '\0'); } } if (i % 16 != 0) @@ -3395,7 +3221,7 @@ void parse_login(int fd) FPRINTF(logfp, " %s\n", tmpstr); } FPRINTF(logfp, "\n"); - fclose_(logfp); + fclose(logfp); } } LOGIN_LOG("End of connection, unknown packet (ip: %s)\n", ip); @@ -3410,12 +3236,12 @@ void parse_login(int fd) // Reading Lan Support configuration //---------------------------------- static -int login_lan_config_read(const char *lancfgName) +int login_lan_config_read(ZString lancfgName) { struct hostent *h = NULL; // set default configuration - strcpy(lan_char_ip, "127.0.0.1"); + lan_char_ip = stringish("127.0.0.1"); subneti[0] = 127; subneti[1] = 0; subneti[2] = 0; @@ -3423,7 +3249,7 @@ int login_lan_config_read(const char *lancfgName) for (int j = 0; j < 4; j++) subnetmaski[j] = 255; - std::ifstream in(lancfgName); + std::ifstream in(lancfgName.c_str()); if (!in.is_open()) { @@ -3434,10 +3260,11 @@ int login_lan_config_read(const char *lancfgName) PRINTF("---Start reading Lan Support configuration file\n"); - std::string line; - while (std::getline(in, line)) + FString line; + while (io::getline(in, line)) { - std::string w1, w2; + SString w1; + TString w2; if (!split_key_value(line, &w1, &w2)) continue; @@ -3447,7 +3274,7 @@ int login_lan_config_read(const char *lancfgName) h = gethostbyname(w2.c_str()); if (h != NULL) { - sprintf(lan_char_ip, "%d.%d.%d.%d", + SNPRINTF(lan_char_ip, 16, "%d.%d.%d.%d", static_cast(h->h_addr[0]), static_cast(h->h_addr[1]), static_cast(h->h_addr[2]), @@ -3455,7 +3282,7 @@ int login_lan_config_read(const char *lancfgName) } else { - strzcpy(lan_char_ip, w2.c_str(), sizeof(lan_char_ip)); + lan_char_ip = stringish(w2); } PRINTF("LAN IP of char-server: %s.\n", lan_char_ip); } @@ -3499,7 +3326,8 @@ int login_lan_config_read(const char *lancfgName) } else { - PRINTF("WARNING: unknown lan-config key: %s\n", w1); + FString w1z = w1; + PRINTF("WARNING: unknown lan-config key: %s\n", w1z); } } @@ -3513,7 +3341,7 @@ int login_lan_config_read(const char *lancfgName) // sub-network check of the char-server { unsigned char p[4]; - sscanf(lan_char_ip, "%hhu.%hhu.%hhu.%hhu", + SSCANF(lan_char_ip, "%hhu.%hhu.%hhu.%hhu", &p[0], &p[1], &p[2], &p[3]); PRINTF("LAN test of LAN IP of the char-server: "); if (lan_ip_check(p) == 0) @@ -3532,9 +3360,9 @@ int login_lan_config_read(const char *lancfgName) // Reading general configuration file //----------------------------------- static -int login_config_read(const char *cfgName) +int login_config_read(ZString cfgName) { - std::ifstream in(cfgName); + std::ifstream in(cfgName.c_str()); if (!in.is_open()) { PRINTF("Configuration file (%s) not found.\n", cfgName); @@ -3543,20 +3371,21 @@ int login_config_read(const char *cfgName) PRINTF("---Start reading of Login Server configuration file (%s)\n", cfgName); - std::string line; - while (std::getline(in, line)) + FString line; + while (io::getline(in, line)) { - std::string w1, w2; + SString w1; + TString w2; if (!split_key_value(line, &w1, &w2)) continue; if (w1 == "admin_state") { - admin_state = config_switch(w2.c_str()); + admin_state = config_switch(w2); } else if (w1 == "admin_pass") { - strzcpy(admin_pass, w2.c_str(), sizeof(admin_pass)); + admin_pass = stringish(w2); } else if (w1 == "ladminallowip") { @@ -3573,20 +3402,19 @@ int login_config_read(const char *cfgName) // set to all access_ladmin.push_back(AccessEntry()); } - else if (!w2.empty() + else if (w2 && !(access_ladmin.size() == 1 && access_ladmin.front() == AccessEntry())) { // don't add IP if already 'all' - AccessEntry n; - strzcpy(n.data(), w2.c_str(), sizeof(n)); + AccessEntry n = stringish(w2); access_ladmin.push_back(n); } } } else if (w1 == "gm_pass") { - strzcpy(gm_pass, w2.c_str(), sizeof(gm_pass)); + gm_pass = w2; } else if (w1 == "level_new_gm") { @@ -3594,7 +3422,7 @@ int login_config_read(const char *cfgName) } else if (w1 == "new_account") { - new_account_flag = config_switch(w2.c_str()); + new_account_flag = config_switch(w2); } else if (w1 == "login_port") { @@ -3602,11 +3430,11 @@ int login_config_read(const char *cfgName) } else if (w1 == "account_filename") { - strzcpy(account_filename, w2.c_str(), sizeof(account_filename)); + account_filename = w2; } else if (w1 == "gm_account_filename") { - strzcpy(GM_account_filename, w2.c_str(), sizeof(GM_account_filename)); + GM_account_filename = w2; } else if (w1 == "gm_account_filename_check_timer") { @@ -3614,28 +3442,27 @@ int login_config_read(const char *cfgName) } else if (w1 == "login_log_filename") { - strzcpy(login_log_filename, w2.c_str(), sizeof(login_log_filename)); + login_log_filename = w2; } else if (w1 == "login_log_unknown_packets_filename") { - strzcpy(login_log_unknown_packets_filename, w2.c_str(), - sizeof(login_log_unknown_packets_filename)); + login_log_unknown_packets_filename = w2; } else if (w1 == "save_unknown_packets") { - save_unknown_packets = config_switch(w2.c_str()); + save_unknown_packets = config_switch(w2); } else if (w1 == "display_parse_login") { - display_parse_login = config_switch(w2.c_str()); // 0: no, 1: yes + display_parse_login = config_switch(w2); // 0: no, 1: yes } else if (w1 == "display_parse_admin") { - display_parse_admin = config_switch(w2.c_str()); // 0: no, 1: yes + display_parse_admin = config_switch(w2); // 0: no, 1: yes } else if (w1 == "display_parse_fromchar") { - display_parse_fromchar = config_switch(w2.c_str()); // 0: no, 1: yes (without packet 0x2714), 2: all packets + display_parse_fromchar = config_switch(w2); // 0: no, 1: yes (without packet 0x2714), 2: all packets } else if (w1 == "min_level_to_connect") { @@ -3643,7 +3470,7 @@ int login_config_read(const char *cfgName) } else if (w1 == "add_to_unlimited_account") { - add_to_unlimited_account = config_switch(w2.c_str()); + add_to_unlimited_account = config_switch(w2); } else if (w1 == "start_limited_time") { @@ -3651,7 +3478,7 @@ int login_config_read(const char *cfgName) } else if (w1 == "check_ip_flag") { - check_ip_flag = config_switch(w2.c_str()); + check_ip_flag = config_switch(w2); } else if (w1 == "order") { @@ -3679,13 +3506,12 @@ int login_config_read(const char *cfgName) // set to all access_allow.push_back(AccessEntry()); } - else if (!w2.empty() + else if (w2 && !(access_allow.size() == 1 && access_allow.front() == AccessEntry())) { // don't add IP if already 'all' - AccessEntry n; - strzcpy(n.data(), w2.c_str(), sizeof(n)); + AccessEntry n = stringish(w2); access_allow.push_back(n); } } @@ -3705,20 +3531,19 @@ int login_config_read(const char *cfgName) // set to all access_deny.push_back(AccessEntry()); } - else if (!w2.empty() + else if (w2 && !(access_deny.size() == 1 && access_deny.front() == AccessEntry())) { // don't add IP if already 'all' - AccessEntry n; - strzcpy(n.data(), w2.c_str(), sizeof(n)); + AccessEntry n = stringish(w2); access_deny.push_back(n); } } } else if (w1 == "anti_freeze_enable") { - anti_freeze_enable = config_switch(w2.c_str()); + anti_freeze_enable = config_switch(w2); } else if (w1 == "anti_freeze_interval") { @@ -3728,19 +3553,20 @@ int login_config_read(const char *cfgName) } else if (w1 == "import") { - login_config_read(w2.c_str()); + login_config_read(w2); } else if (w1 == "update_host") { - strzcpy(update_host, w2.c_str(), sizeof(update_host)); + update_host = w2; } else if (w1 == "main_server") { - strzcpy(main_server, w2.c_str(), sizeof(main_server)); + main_server = stringish(w2); } else { - PRINTF("WARNING: unknown login config key: %s\n", w1); + FString w1z = w1; + PRINTF("WARNING: unknown login config key: %s\n", w1z); } } @@ -3763,23 +3589,23 @@ void display_conf_warnings(void) if (admin_state == 1) { - if (admin_pass[0] == '\0') + if (!admin_pass) { PRINTF("***WARNING: Administrator password is void (admin_pass).\n"); } - else if (strcmp(admin_pass, "admin") == 0) + else if (admin_pass == stringish("admin")) { PRINTF("***WARNING: You are using the default administrator password (admin_pass).\n"); PRINTF(" We highly recommend that you change it.\n"); } } - if (gm_pass[0] == '\0') + if (!gm_pass) { PRINTF("***WARNING: 'To GM become' password is void (gm_pass).\n"); PRINTF(" We highly recommend that you set one password.\n"); } - else if (strcmp(gm_pass, "gm") == 0) + else if (gm_pass == "gm") { PRINTF("***WARNING: You are using the default GM password (gm_pass).\n"); PRINTF(" We highly recommend that you change it.\n"); @@ -3910,8 +3736,6 @@ void display_conf_warnings(void) PRINTF(" But, you refuse ALL IP!\n"); } } - - return; } //------------------------------- @@ -3929,13 +3753,13 @@ void save_config_in_log(void) if (admin_state != 1) LOGIN_LOG("- with no remote administration.\n"); - else if (admin_pass[0] == '\0') + else if (!admin_pass) LOGIN_LOG("- with a remote administration with a VOID password.\n"); - else if (strcmp(admin_pass, "admin") == 0) + else if (admin_pass == stringish("admin")) LOGIN_LOG("- with a remote administration with the DEFAULT password.\n"); else LOGIN_LOG("- with a remote administration with the password of %zu character(s).\n", - strlen(admin_pass)); + admin_pass.size()); if (access_ladmin.empty() || (access_ladmin.size() == 1 && access_ladmin.front() == AccessEntry())) { @@ -3945,16 +3769,16 @@ void save_config_in_log(void) { LOGIN_LOG("- to accept following IP for remote administration:\n"); for (const AccessEntry& ae : access_ladmin) - LOGIN_LOG(" %s\n", ae.data()); + LOGIN_LOG(" %s\n", ae); } - if (gm_pass[0] == '\0') + if (!gm_pass) LOGIN_LOG("- with a VOID 'To GM become' password (gm_pass).\n"); - else if (strcmp(gm_pass, "gm") == 0) + else if (gm_pass == "gm") LOGIN_LOG("- with the DEFAULT 'To GM become' password (gm_pass).\n"); else LOGIN_LOG("- with a 'To GM become' password (gm_pass) of %zu character(s).\n", - strlen(gm_pass)); + gm_pass.size()); if (level_new_gm == 0) LOGIN_LOG("- to refuse any creation of GM with @gm.\n"); else @@ -4037,7 +3861,7 @@ void save_config_in_log(void) { LOGIN_LOG("- with the IP security order: 'deny,allow' (allow if not deny). Refused IP are:\n"); for (const AccessEntry& ae : access_deny) - LOGIN_LOG(" %s\n", ae.data()); + LOGIN_LOG(" %s\n", ae); } } else if (access_order == ACO::ALLOW_DENY) @@ -4054,7 +3878,7 @@ void save_config_in_log(void) { LOGIN_LOG("- with the IP security order: 'allow,deny' (deny if not allow). Authorised IP are:\n"); for (const AccessEntry& ae : access_allow) - LOGIN_LOG(" %s\n", ae.data()); + LOGIN_LOG(" %s\n", ae); } } else @@ -4078,11 +3902,11 @@ void save_config_in_log(void) { LOGIN_LOG(" Authorised IP are:\n"); for (const AccessEntry& ae : access_allow) - LOGIN_LOG(" %s\n", ae.data()); + LOGIN_LOG(" %s\n", ae); } LOGIN_LOG(" Refused IP are:\n"); for (const AccessEntry& ae : access_deny) - LOGIN_LOG(" %s\n", ae.data()); + LOGIN_LOG(" %s\n", ae); } } } @@ -4111,13 +3935,19 @@ void term_func(void) //------------------------------ // Main function of login-server //------------------------------ -int do_init(int argc, char **argv) +int do_init(int argc, ZString *argv) { // read login-server configuration - login_config_read((argc > 1) ? argv[1] : LOGIN_CONF_NAME); + if (argc > 1) + login_config_read(argv[1]); + else + login_config_read(LOGIN_CONF_NAME); display_conf_warnings(); // not in login_config_read, because we can use 'import' option, and display same message twice or more save_config_in_log(); // not before, because log file name can be changed - login_lan_config_read((argc > 1) ? argv[1] : LAN_CONF_NAME); + if (argc > 2) + login_lan_config_read(argv[2]); + else + login_lan_config_read(LAN_CONF_NAME); for (int i = 0; i < AUTH_FIFO_SIZE; i++) auth_fifo[i].delflag = 1; diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp index 1926f83..18ea0e0 100644 --- a/src/map/atcommand.cpp +++ b/src/map/atcommand.cpp @@ -8,9 +8,12 @@ #include "../common/core.hpp" #include "../common/cxxstdio.hpp" +#include "../common/extract.hpp" +#include "../common/human_time_diff.hpp" +#include "../common/io.hpp" #include "../common/mmo.hpp" -#include "../common/random.hpp" #include "../common/nullpo.hpp" +#include "../common/random.hpp" #include "../common/socket.hpp" #include "../common/timer.hpp" #include "../common/utils2.hpp" @@ -34,7 +37,7 @@ #include "../poison.hpp" #define ATCOMMAND_FUNC(x) static \ -int atcommand_##x(const int fd, dumb_ptr sd, const char *, const char *message) +int atcommand_##x(const int fd, dumb_ptr sd, ZString message) ATCOMMAND_FUNC(setup); ATCOMMAND_FUNC(broadcast); ATCOMMAND_FUNC(localbroadcast); @@ -175,13 +178,16 @@ ATCOMMAND_FUNC(doomspot); *AtCommandInfo atcommand_info[]構造体の定義 *------------------------------------------ */ - struct AtCommandInfo { - const char *command; + ZString command; int level; - int(*proc)(const int, dumb_ptr, - const char *command, const char *message); + int (*proc)(const int fd, dumb_ptr sd, ZString message); + + + AtCommandInfo(ZString c, int l, int (*p)(const int, dumb_ptr, ZString)) + : command(c), level(l), proc(p) + {} }; // First char of commands is configured in atcommand_athena.conf. Leave @ in this list for default value. @@ -335,9 +341,50 @@ AtCommandInfo atcommand_info[] = {"@doomspot", 60, atcommand_doomspot}, // add new commands before this line - {NULL, 1, NULL} + {ZString(), 1, nullptr} }; +// If your last arg is not a ZString, you probably wanted extract() +// but not always ... +static +bool asplit(ZString raw, ZString *last) +{ + *last = raw; + return true; +} + +// but this case is just so common and useful. In fact, is the previous ever used otherwise? +static +bool asplit(ZString raw, CharName *last) +{ + if (raw.size() < 4 || raw.size() > 23) + return false; + *last = stringish(raw); + return true; +} + +// huh. +static +bool asplit(ZString raw, NpcName *last) +{ + if (!raw || raw.size() > 23) + return false; + *last = stringish(raw); + return true; +} + +// This differs from extract() in that it does not consume extra spaces. +template::type> +bool asplit(ZString raw, F *first_arg, R *... rest_args) +{ + ZString::iterator it = std::find(raw.begin(), raw.end(), ' '); + XString frist = raw.xislice_h(it); + while (*it == ' ') + ++it; + ZString rest = raw.xislice_t(it); + return extract(frist, first_arg) && asplit(rest, rest_args...); +} + /*========================================== * get_atcommand_level @コマンドの必要レベルを取得 *------------------------------------------ @@ -357,28 +404,28 @@ FILE *get_gm_log(); /*======================================== * At-command logging */ -void log_atcommand(dumb_ptr sd, const_string cmd) +void log_atcommand(dumb_ptr sd, XString cmd) { FILE *fp = get_gm_log(); if (!fp) return; timestamp_seconds_buffer tmpstr; stamp_time(tmpstr); - fprintf(fp, "[%s] %s(%d,%d) %s(%d) : ", + FPRINTF(fp, "[%s] %s(%d,%d) %s(%d) : ", tmpstr, sd->bl_m->name_, sd->bl_x, sd->bl_y, sd->status.name, sd->status.account_id); fwrite(cmd.data(), 1, cmd.size(), fp); } -std::string gm_logfile_name; +FString gm_logfile_name; /*========================================== * Log a timestamped line to GM log file *------------------------------------------ */ FILE *get_gm_log() { - if (gm_logfile_name.empty()) + if (!gm_logfile_name) return NULL; struct tm ctime = TimeT::now(); @@ -393,65 +440,56 @@ FILE *get_gm_log() return gm_logfile; last_logfile_nr = logfile_nr; - std::string fullname = STRPRINTF("%s.%04d-%02d", + FString fullname = STRPRINTF("%s.%04d-%02d", gm_logfile_name, year, month); if (gm_logfile) - fclose_(gm_logfile); + fclose(gm_logfile); - gm_logfile = fopen_(fullname.c_str(), "a"); + gm_logfile = fopen(fullname.c_str(), "a"); if (!gm_logfile) { perror("GM log file"); - gm_logfile_name.clear(); + gm_logfile_name = FString(); } return gm_logfile; } static -AtCommandInfo *atcommand(const int level, const char *message); +AtCommandInfo *atcommand(const int level, ZString message); /*========================================== *is_atcommand @コマンドに存在するかどうか確認する *------------------------------------------ */ bool is_atcommand(const int fd, dumb_ptr sd, - const char *message, int gmlvl) + ZString message, int gmlvl) { nullpo_retr(false, sd); - if (!message || message[0] != '@') + if (!message.startswith('@')) return false; AtCommandInfo *info = atcommand(gmlvl > 0 ? gmlvl : pc_isGM(sd), message); if (!info) { - std::string output = STRPRINTF("GM command not found: %s", + FString output = STRPRINTF("GM command not found: %s", message); clif_displaymessage(fd, output); return true; // don't show in chat } { - const char *str = message; - const char *p = message; - // split the first word - while (*p && !isspace(*p)) - p++; - size_t len = p - str; - char command[len + 1]; - strzcpy(command, str, len + 1); - // skip the spaces; pass as argv - while (isspace(*p)) - p++; + XString command; + ZString arg; + asplit(message, &command, &arg); { - if (info->proc(fd, sd, command, p) != 0) + if (info->proc(fd, sd, arg) != 0) { // Command can not be executed - const char *command_ = command; - std::string output = STRPRINTF("%s failed.", command_); + FString output = STRPRINTF("%s failed.", FString(command)); clif_displaymessage(fd, output); } else @@ -469,28 +507,27 @@ bool is_atcommand(const int fd, dumb_ptr sd, * *------------------------------------------ */ -AtCommandInfo *atcommand(const int level, const char *message) +AtCommandInfo *atcommand(const int level, ZString message) { - const char *p = message; + ZString p = message; if (battle_config.atc_gmonly != 0 && !level) // level = pc_isGM(sd) return nullptr; - if (!p || !*p) + if (!p) { FPRINTF(stderr, "at command message is empty\n"); return nullptr; } - if (*p == '@') - { // check first char. - char command[101]; + if (p.startswith('@')) + { + ZString::iterator space = std::find(p.begin(), p.end(), ' '); + XString command = p.xislice_h(space); int i = 0; - sscanf(p, "%100s", command); - command[sizeof(command) - 1] = '\0'; while (atcommand_info[i].command) { - if (strcasecmp(command, atcommand_info[i].command) == 0 + if (command == atcommand_info[i].command && level >= atcommand_info[i].level) { return &atcommand_info[i]; @@ -522,12 +559,10 @@ void atkillmonster_sub(dumb_ptr bl, int flag) *------------------------------------------ */ static -AtCommandInfo *get_atcommandinfo_byname(const char *name) +AtCommandInfo *get_atcommandinfo_byname(XString name) { - int i; - - for (i = 0; atcommand_info[i].command; i++) - if (strcasecmp(atcommand_info[i].command + 1, name) == 0) + for (int i = 0; atcommand_info[i].command; i++) + if (atcommand_info[i].command.xslice_t(1) == name) return &atcommand_info[i]; return NULL; @@ -537,39 +572,36 @@ AtCommandInfo *get_atcommandinfo_byname(const char *name) * *------------------------------------------ */ -int atcommand_config_read(const char *cfgName) +int atcommand_config_read(ZString cfgName) { - char line[1024], w1[1024], w2[1024]; - AtCommandInfo *p; - FILE *fp; - - if ((fp = fopen_(cfgName, "r")) == NULL) + std::ifstream in(cfgName.c_str()); + if (!in.is_open()) { PRINTF("At commands configuration file not found: %s\n", cfgName); return 1; } - while (fgets(line, sizeof(line) - 1, fp)) + FString line; + while (io::getline(in, line)) { - if (line[0] == '/' && line[1] == '/') - continue; - - if (sscanf(line, "%1023[^:]:%1023s", w1, w2) != 2) + SString w1; + TString w2; + if (!split_key_value(line, &w1, &w2)) continue; - p = get_atcommandinfo_byname(w1); + AtCommandInfo *p = get_atcommandinfo_byname(w1); if (p != NULL) { - p->level = atoi(w2); + p->level = atoi(w2.c_str()); if (p->level > 100) p->level = 100; else if (p->level < 0) p->level = 0; } - - if (strcasecmp(w1, "import") == 0) + else if (w1 == "import") atcommand_config_read(w2); + else + PRINTF("%s: bad line: %s\n", cfgName, line); } - fclose_(fp); return 0; } @@ -585,42 +617,41 @@ int atcommand_config_read(const char *cfgName) *------------------------------------------ */ int atcommand_setup(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { - char character[100] {}; int level = 1; + CharName character; - if (!message || !*message - || sscanf(message, "%d %99[^\n]", &level, character) < 2) + if (!asplit(message, &level, &character)) { clif_displaymessage(fd, "Usage: @setup "); return -1; } level--; - std::string buf; + FString buf; buf = STRPRINTF("-255 %s", character); - atcommand_character_baselevel(fd, sd, "@charbaselvl", buf.c_str()); + atcommand_character_baselevel(fd, sd, buf); buf = STRPRINTF("%d %s", level, character); - atcommand_character_baselevel(fd, sd, "@charbaselvl", buf.c_str()); + atcommand_character_baselevel(fd, sd, buf); // Emote skill buf = STRPRINTF("1 1 %s", character); - atcommand_skill_learn(fd, sd, "@skill-learn", buf.c_str()); + atcommand_skill_learn(fd, sd, buf); // Trade skill buf = STRPRINTF("2 1 %s", character); - atcommand_skill_learn(fd, sd, "@skill-learn", buf.c_str()); + atcommand_skill_learn(fd, sd, buf); // Party skill STRPRINTF("2 2 %s", character); - atcommand_skill_learn(fd, sd, "@skill-learn", buf.c_str()); + atcommand_skill_learn(fd, sd, buf); STRPRINTF("018-1.gat 24 98 %s", character); - atcommand_charwarp(fd, sd, "@charwarp", buf.c_str()); + atcommand_charwarp(fd, sd, buf); - return (0); + return 0; } @@ -629,19 +660,16 @@ int atcommand_setup(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_charwarp(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { - char map_name[100] {}; - char character[100] {}; + MapName map_name; + CharName character; int x = 0, y = 0; - dumb_ptr pl_sd; - if (!message || !*message - || sscanf(message, "%99s %d %d %99[^\n]", map_name, &x, &y, - character) < 4) + if (!asplit(message, &map_name, &x, &y, &character)) { clif_displaymessage(fd, - "Usage: @charwarp/@rura+ "); + "Usage: @charwarp/@rura+ "); return -1; } @@ -649,13 +677,13 @@ int atcommand_charwarp(const int fd, dumb_ptr sd, x = random_::in(1, 399); if (y <= 0) y = random_::in(1, 399); - if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < 13) // 16 - 4 (.gat) - strcat(map_name, ".gat"); - if ((pl_sd = map_nick2sd(character)) != NULL) + dumb_ptr pl_sd = map_nick2sd(character); + if (pl_sd) { if (pc_isGM(sd) >= pc_isGM(pl_sd)) - { // you can rura+ only lower or same GM level + { + // you can rura+ only lower or same GM level if (x > 0 && x < 800 && y > 0 && y < 800) { map_local *m = map_mapname2mapid(map_name); @@ -663,14 +691,14 @@ int atcommand_charwarp(const int fd, dumb_ptr sd, && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { clif_displaymessage(fd, - "You are not authorised to warp someone to this map."); + "You are not authorised to warp someone to this map."); return -1; } if (pl_sd->bl_m && pl_sd->bl_m->flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { clif_displaymessage(fd, - "You are not authorised to warp this player from its actual map."); + "You are not authorised to warp this player from its actual map."); return -1; } if (pc_setpos(pl_sd, map_name, x, y, BeingRemoveWhy::WARPED) == 0) @@ -710,16 +738,16 @@ int atcommand_charwarp(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_warp(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { - char map_name[100] {}; + MapName map_name; int x = 0, y = 0; - if (!message || !*message - || sscanf(message, "%99s %d %d", map_name, &x, &y) < 1) + if (!message + || !extract(message, record<' ', 1>(&map_name, &x, &y))) { clif_displaymessage(fd, - "Please, enter a map (usage: @warp )."); + "Please, enter a map (usage: @warp )."); return -1; } @@ -728,9 +756,6 @@ int atcommand_warp(const int fd, dumb_ptr sd, if (y <= 0) y = random_::in(1, 399); - if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < 13) // 16 - 4 (.gat) - strcat(map_name, ".gat"); - if (x > 0 && x < 800 && y > 0 && y < 800) { map_local *m = map_mapname2mapid(map_name); @@ -738,14 +763,14 @@ int atcommand_warp(const int fd, dumb_ptr sd, && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { clif_displaymessage(fd, - "You are not authorised to warp you to this map."); + "You are not authorised to warp you to this map."); return -1; } if (sd->bl_m && sd->bl_m->flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { clif_displaymessage(fd, - "You are not authorised to warp you from your actual map."); + "You are not authorised to warp you from your actual map."); return -1; } if (pc_setpos(sd, map_name, x, y, BeingRemoveWhy::WARPED) == 0) @@ -770,20 +795,18 @@ int atcommand_warp(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_where(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { - char character[100] {}; - dumb_ptr pl_sd; + CharName character; + extract(message, &character); - if (sscanf(message, "%99[^\n]", character) < 1) - strcpy(character, sd->status.name); - - if ((pl_sd = map_nick2sd(character)) != NULL && + dumb_ptr pl_sd = character.to__actual() ? map_nick2sd(character) : sd; + if (pl_sd != NULL && !((battle_config.hide_GM_session || bool(pl_sd->status.option & Option::HIDE)) && (pc_isGM(pl_sd) > pc_isGM(sd)))) { // you can look only lower or same level - std::string output = STRPRINTF("%s: %s (%d,%d)", + FString output = STRPRINTF("%s: %s (%d,%d)", pl_sd->status.name, pl_sd->mapname_, pl_sd->bl_x, pl_sd->bl_y); clif_displaymessage(fd, output); @@ -802,36 +825,36 @@ int atcommand_where(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_goto(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { - char character[100] {}; - dumb_ptr pl_sd; + CharName character; - if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) + if (!asplit(message, &character)) { clif_displaymessage(fd, - "Please, enter a player name (usage: @jumpto/@warpto/@goto )."); + "Please, enter a player name (usage: @jumpto/@warpto/@goto )."); return -1; } - if ((pl_sd = map_nick2sd(character)) != NULL) + dumb_ptr pl_sd = map_nick2sd(character); + if (pl_sd != NULL) { if (pl_sd->bl_m && pl_sd->bl_m->flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { clif_displaymessage(fd, - "You are not authorised to warp you to the map of this player."); + "You are not authorised to warp you to the map of this player."); return -1; } if (sd->bl_m && sd->bl_m->flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { clif_displaymessage(fd, - "You are not authorised to warp you from your actual map."); + "You are not authorised to warp you from your actual map."); return -1; } pc_setpos(sd, pl_sd->mapname_, pl_sd->bl_x, pl_sd->bl_y, BeingRemoveWhy::WARPED); - std::string output = STRPRINTF("Jump to %s", character); + FString output = STRPRINTF("Jump to %s", character); clif_displaymessage(fd, output); } else @@ -848,10 +871,11 @@ int atcommand_goto(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_jump(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { int x = 0, y = 0; - sscanf(message, "%d %d", &x, &y); + // may fail + extract(message, record<' '>(&x, &y)); if (x <= 0) x = random_::in(1, 399); @@ -863,18 +887,18 @@ int atcommand_jump(const int fd, dumb_ptr sd, && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { clif_displaymessage(fd, - "You are not authorised to warp you to your actual map."); + "You are not authorised to warp you to your actual map."); return -1; } if (sd->bl_m && sd->bl_m->flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { clif_displaymessage(fd, - "You are not authorised to warp you from your actual map."); + "You are not authorised to warp you from your actual map."); return -1; } pc_setpos(sd, sd->mapname_, x, y, BeingRemoveWhy::WARPED); - std::string output = STRPRINTF("Jump to %d %d", x, y); + FString output = STRPRINTF("Jump to %d %d", x, y); clif_displaymessage(fd, output); } else @@ -891,17 +915,12 @@ int atcommand_jump(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_who(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { int count; int pl_GM_level, GM_level; - char match_text[100] {}; - char player_name[24] {}; - - if (sscanf(message, "%99[^\n]", match_text) < 1) - strcpy(match_text, ""); - for (int j = 0; match_text[j]; j++) - match_text[j] = tolower(match_text[j]); + VString<23> match_text = message; + match_text = match_text.to_lower(); count = 0; GM_level = pc_isGM(sd); @@ -919,13 +938,11 @@ int atcommand_who(const int fd, dumb_ptr sd, && (pl_GM_level > GM_level))) { // you can look only lower or same level - strzcpy(player_name, pl_sd->status.name, 24); - for (int j = 0; player_name[j]; j++) - player_name[j] = tolower(player_name[j]); - if (strstr(player_name, match_text) != NULL) + VString<23> player_name = pl_sd->status.name.to__lower(); + if (player_name.contains_seq(match_text)) { // search with no case sensitive - std::string output; + FString output; if (pl_GM_level > 0) output = STRPRINTF( "Name: %s (GM:%d) | Location: %s %d %d", @@ -949,7 +966,7 @@ int atcommand_who(const int fd, dumb_ptr sd, clif_displaymessage(fd, "1 player found."); else { - std::string output = STRPRINTF("%d players found.", count); + FString output = STRPRINTF("%d players found.", count); clif_displaymessage(fd, output); } @@ -961,18 +978,14 @@ int atcommand_who(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_whogroup(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { int count; int pl_GM_level, GM_level; - char match_text[100] {}; - char player_name[24] {}; struct party *p; - if (sscanf(message, "%99[^\n]", match_text) < 1) - strcpy(match_text, ""); - for (int j = 0; match_text[j]; j++) - match_text[j] = tolower(match_text[j]); + VString<23> match_text = message; + match_text = match_text.to_lower(); count = 0; GM_level = pc_isGM(sd); @@ -990,15 +1003,13 @@ int atcommand_whogroup(const int fd, dumb_ptr sd, && (pl_GM_level > GM_level))) { // you can look only lower or same level - strzcpy(player_name, pl_sd->status.name, 24); - for (int j = 0; player_name[j]; j++) - player_name[j] = tolower(player_name[j]); - if (strstr(player_name, match_text) != NULL) + VString<23> player_name = pl_sd->status.name.to__lower(); + if (player_name.contains_seq(match_text)) { // search with no case sensitive p = party_search(pl_sd->status.party_id); - const char *temp0 = p ? p->name : "None"; - std::string output; + PartyName temp0 = p ? p->name : stringish("None"); + FString output; if (pl_GM_level > 0) output = STRPRINTF( "Name: %s (GM:%d) | Party: '%s'", @@ -1016,7 +1027,7 @@ int atcommand_whogroup(const int fd, dumb_ptr sd, clif_displaymessage(fd, "1 player found."); else { - std::string output = STRPRINTF("%d players found.", count); + FString output = STRPRINTF("%d players found.", count); clif_displaymessage(fd, output); } @@ -1028,20 +1039,15 @@ int atcommand_whogroup(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_whomap(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { int count; int pl_GM_level, GM_level; map_local *map_id; - char map_name[100] {}; - if (!message || !*message) - map_id = sd->bl_m; - else { - sscanf(message, "%99s", map_name); - if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < 13) // 16 - 4 (.gat) - strcat(map_name, ".gat"); + MapName map_name; + extract(message, &map_name); map_id = map_mapname2mapid(map_name); if (map_id == nullptr) map_id = sd->bl_m; @@ -1064,7 +1070,7 @@ int atcommand_whomap(const int fd, dumb_ptr sd, { // you can look only lower or same level if (pl_sd->bl_m == map_id) { - std::string output; + FString output; if (pl_GM_level > 0) output = STRPRINTF( "Name: %s (GM:%d) | Location: %s %d %d", @@ -1082,7 +1088,7 @@ int atcommand_whomap(const int fd, dumb_ptr sd, } } - std::string output = STRPRINTF("%d players found in map '%s'.", + FString output = STRPRINTF("%d players found in map '%s'.", count, map_id->name_); clif_displaymessage(fd, output); @@ -1094,21 +1100,16 @@ int atcommand_whomap(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_whomapgroup(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { int count; int pl_GM_level, GM_level; - char map_name[100] {}; struct party *p; map_local *map_id; - if (!message || !*message) - map_id = sd->bl_m; - else { - sscanf(message, "%99s", map_name); - if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < 13) // 16 - 4 (.gat) - strcat(map_name, ".gat"); + MapName map_name; + extract(message, &map_name); map_id = map_mapname2mapid(map_name); if (map_id == nullptr) map_id = sd->bl_m; @@ -1133,8 +1134,8 @@ int atcommand_whomapgroup(const int fd, dumb_ptr sd, if (pl_sd->bl_m == map_id) { p = party_search(pl_sd->status.party_id); - const char *temp0 = p ? p->name : "None"; - std::string output; + PartyName temp0 = p ? p->name : stringish("None"); + FString output; if (pl_GM_level > 0) output = STRPRINTF("Name: %s (GM:%d) | Party: '%s'", pl_sd->status.name, pl_GM_level, temp0); @@ -1148,7 +1149,7 @@ int atcommand_whomapgroup(const int fd, dumb_ptr sd, } } - std::string output; + FString output; if (count == 0) output = STRPRINTF("No player found in map '%s'.", map_id->name_); else if (count == 1) @@ -1167,18 +1168,14 @@ int atcommand_whomapgroup(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_whogm(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { int count; int pl_GM_level, GM_level; - char match_text[100] {}; - char player_name[24] {}; struct party *p; - if (sscanf(message, "%99[^\n]", match_text) < 1) - strcpy(match_text, ""); - for (int j = 0; match_text[j]; j++) - match_text[j] = tolower(match_text[j]); + VString<23> match_text = message; + match_text = match_text.to_lower(); count = 0; GM_level = pc_isGM(sd); @@ -1198,13 +1195,11 @@ int atcommand_whogm(const int fd, dumb_ptr sd, && (pl_GM_level > GM_level))) { // you can look only lower or same level - strzcpy(player_name, pl_sd->status.name, 24); - for (int j = 0; player_name[j]; j++) - player_name[j] = tolower(player_name[j]); - if (strstr(player_name, match_text) != NULL) + VString<23> player_name = pl_sd->status.name.to__lower(); + if (player_name.contains_seq(match_text)) { // search with no case sensitive - std::string output; + FString output; output = STRPRINTF( "Name: %s (GM:%d) | Location: %s %d %d", pl_sd->status.name, pl_GM_level, @@ -1217,7 +1212,7 @@ int atcommand_whogm(const int fd, dumb_ptr sd, pl_sd->status.job_level); clif_displaymessage(fd, output); p = party_search(pl_sd->status.party_id); - const char *temp0 = p ? p->name : "None"; + PartyName temp0 = p ? p->name : stringish("None"); output = STRPRINTF( " Party: '%s'", temp0); @@ -1235,7 +1230,7 @@ int atcommand_whogm(const int fd, dumb_ptr sd, clif_displaymessage(fd, "1 GM found."); else { - std::string output = STRPRINTF("%d GMs found.", count); + FString output = STRPRINTF("%d GMs found.", count); clif_displaymessage(fd, output); } @@ -1247,7 +1242,7 @@ int atcommand_whogm(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_save(const int fd, dumb_ptr sd, - const char *, const char *) + ZString) { nullpo_retr(-1, sd); @@ -1264,7 +1259,7 @@ int atcommand_save(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_load(const int fd, dumb_ptr sd, - const char *, const char *) + ZString) { map_local *m = map_mapname2mapid(sd->status.save_point.map_); if (m != nullptr && m->flag.nowarpto @@ -1294,11 +1289,11 @@ int atcommand_load(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_speed(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { - if (!message || !*message) + if (!message) { - std::string output = STRPRINTF( + FString output = STRPRINTF( "Please, enter a speed value (usage: @speed <%d-%d>).", static_cast(MIN_WALK_SPEED.count()), static_cast(MAX_WALK_SPEED.count())); @@ -1306,7 +1301,7 @@ int atcommand_speed(const int fd, dumb_ptr sd, return -1; } - interval_t speed = static_cast(atoi(message)); + interval_t speed = static_cast(atoi(message.c_str())); if (speed >= MIN_WALK_SPEED && speed <= MAX_WALK_SPEED) { sd->speed = speed; @@ -1317,7 +1312,7 @@ int atcommand_speed(const int fd, dumb_ptr sd, } else { - std::string output = STRPRINTF( + FString output = STRPRINTF( "Please, enter a valid speed value (usage: @speed <%d-%d>).", static_cast(MIN_WALK_SPEED.count()), static_cast(MAX_WALK_SPEED.count())); @@ -1333,7 +1328,7 @@ int atcommand_speed(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_storage(const int fd, dumb_ptr sd, - const char *, const char *) + ZString) { struct storage *stor; //changes from Freya/Yor nullpo_retr(-1, sd); @@ -1361,24 +1356,21 @@ int atcommand_storage(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_option(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { - int param1_ = 0, param2_ = 0, param3_ = 0; nullpo_retr(-1, sd); - if (!message || !*message - || sscanf(message, "%d %d %d", ¶m1_, ¶m2_, ¶m3_) < 1 - || param1_ < 0 || param2_ < 0 || param3_ < 0) + Opt1 param1 = Opt1::ZERO; + Opt2 param2 = Opt2::ZERO; + Option param3 = Option::ZERO; + + if (!extract(message, record<',', 1>(¶m1, ¶m2, ¶m3))) { clif_displaymessage(fd, - "Please, enter at least a option (usage: @option )."); + "Please, enter at least a option (usage: @option )."); return -1; } - Opt1 param1 = Opt1(param1_); - Opt2 param2 = Opt2(param2_); - Option param3 = Option(param3_); - sd->opt1 = param1; sd->opt2 = param2; sd->status.option = param3; @@ -1395,7 +1387,7 @@ int atcommand_option(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_hide(const int fd, dumb_ptr sd, - const char *, const char *) + ZString) { if (bool(sd->status.option & Option::HIDE)) { @@ -1417,7 +1409,7 @@ int atcommand_hide(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_die(const int fd, dumb_ptr sd, - const char *, const char *) + ZString) { pc_damage(NULL, sd, sd->status.hp + 1); clif_displaymessage(fd, "A pity! You've died."); @@ -1430,19 +1422,19 @@ int atcommand_die(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_kill(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { - char character[100] {}; - dumb_ptr pl_sd; + CharName character; - if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) + if (!asplit(message, &character)) { clif_displaymessage(fd, - "Please, enter a player name (usage: @kill )."); + "Please, enter a player name (usage: @kill )."); return -1; } - if ((pl_sd = map_nick2sd(character)) != NULL) + dumb_ptr pl_sd = map_nick2sd(character); + if (pl_sd != NULL) { if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can kill only lower or same level @@ -1469,7 +1461,7 @@ int atcommand_kill(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_alive(const int fd, dumb_ptr sd, - const char *, const char *) + ZString) { sd->status.hp = sd->status.max_hp; sd->status.sp = sd->status.max_sp; @@ -1489,12 +1481,12 @@ int atcommand_alive(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_kami(const int fd, dumb_ptr, - const char *, const char *message) + ZString message) { - if (!message || !*message) + if (!message) { clif_displaymessage(fd, - "Please, enter a message (usage: @kami )."); + "Please, enter a message (usage: @kami )."); return -1; } @@ -1508,11 +1500,11 @@ int atcommand_kami(const int fd, dumb_ptr, *------------------------------------------ */ int atcommand_heal(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { int hp = 0, sp = 0; // [Valaris] thanks to fov - sscanf(message, "%d %d", &hp, &sp); + extract(message, record<' '>(&hp, &sp)); if (hp == 0 && sp == 0) { @@ -1556,18 +1548,17 @@ int atcommand_heal(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_item(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { - char item_name[100] {}; + ItemName item_name; int number = 0, item_id; struct item_data *item_data; int get_count, i; - if (!message || !*message - || sscanf(message, "%99s %d", item_name, &number) < 1) + if (!extract(message, record<' ', 1>(&item_name, &number))) { clif_displaymessage(fd, - "Please, enter an item name/id (usage: @item [quantity])."); + "Please, enter an item name/id (usage: @item [quantity])."); return -1; } @@ -1576,7 +1567,7 @@ int atcommand_item(const int fd, dumb_ptr sd, item_id = 0; if ((item_data = itemdb_searchname(item_name)) != NULL || - (item_data = itemdb_exists(atoi(item_name))) != NULL) + (item_data = itemdb_exists(atoi(item_name.c_str()))) != NULL) item_id = item_data->nameid; if (item_id >= 500) @@ -1615,7 +1606,7 @@ int atcommand_item(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_itemreset(const int fd, dumb_ptr sd, - const char *, const char *) + ZString) { int i; @@ -1635,7 +1626,7 @@ int atcommand_itemreset(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_itemcheck(const int, dumb_ptr sd, - const char *, const char *) + ZString) { pc_checkitem(sd); @@ -1647,14 +1638,14 @@ int atcommand_itemcheck(const int, dumb_ptr sd, *------------------------------------------ */ int atcommand_baselevelup(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { int level, i; - if (!message || !*message || (level = atoi(message)) == 0) + if (!extract(message, &level) || !level) { clif_displaymessage(fd, - "Please, enter a level adjustement (usage: @blvl )."); + "Please, enter a level adjustement (usage: @blvl )."); return -1; } @@ -1713,14 +1704,14 @@ int atcommand_baselevelup(const int fd, dumb_ptr sd, // TODO: merge this with pc_setparam(SP::JOBLEVEL) // then fix the funny 50 and/or 10 limitation. int atcommand_joblevelup(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { int up_level = 50, level; - if (!message || !*message || (level = atoi(message)) == 0) + if (!extract(message, &level) || !level) { clif_displaymessage(fd, - "Please, enter a level adjustement (usage: @jlvl )."); + "Please, enter a level adjustement (usage: @jlvl )."); return -1; } @@ -1775,20 +1766,23 @@ int atcommand_joblevelup(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_help(const int fd, dumb_ptr sd, - const char *, const char *) + ZString) { - std::ifstream in(help_txt); + std::ifstream in(help_txt.c_str()); if (in.is_open()) { clif_displaymessage(fd, "Help commands:"); int gm_level = pc_isGM(sd); - std::string line; - while (std::getline(in, line)) + FString line; + while (io::getline(in, line)) { - std::string w1, w2; + SString w1; + TString w2; if (!split_key_value(line, &w1, &w2)) continue; - if (gm_level >= atoi(w1.c_str())) + int level; + extract(w1, &level); + if (gm_level >= level) clif_displaymessage(fd, w2); } } @@ -1806,14 +1800,12 @@ int atcommand_help(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_gm(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { - char password[100] {}; - - if (!message || !*message || sscanf(message, "%99[^\n]", password) < 1) + if (!message) { clif_displaymessage(fd, - "Please, enter a password (usage: @gm )."); + "Please, enter a password (usage: @gm )."); return -1; } @@ -1823,8 +1815,7 @@ int atcommand_gm(const int fd, dumb_ptr sd, return -1; } else - chrif_changegm(sd->status.account_id, password, - strlen(password) + 1); + chrif_changegm(sd->status.account_id, message); return 0; } @@ -1834,7 +1825,7 @@ int atcommand_gm(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_pvpoff(const int fd, dumb_ptr sd, - const char *, const char *) + ZString) { if (battle_config.pk_mode) { //disable command if server is in PK mode [Valaris] @@ -1874,7 +1865,7 @@ int atcommand_pvpoff(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_pvpon(const int fd, dumb_ptr sd, - const char *, const char *) + ZString) { if (battle_config.pk_mode) { //disable command if server is in PK mode [Valaris] @@ -1918,15 +1909,13 @@ int atcommand_pvpon(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_model(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { int hair_style = 0, hair_color = 0, cloth_color = 0; - if (!message || !*message - || sscanf(message, "%d %d %d", &hair_style, &hair_color, - &cloth_color) < 1) + if (!extract(message, record<' ', 1>(&hair_style, &hair_color, &cloth_color))) { - std::string output = STRPRINTF( + FString output = STRPRINTF( "Please, enter at least a value (usage: @model ).", MIN_HAIR_STYLE, MAX_HAIR_STYLE, MIN_HAIR_COLOR, MAX_HAIR_COLOR, @@ -1960,13 +1949,13 @@ int atcommand_model(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_dye(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { int cloth_color = 0; - if (!message || !*message || sscanf(message, "%d", &cloth_color) < 1) + if (!extract(message, &cloth_color)) { - std::string output = STRPRINTF( + FString output = STRPRINTF( "Please, enter a clothes color (usage: @dye/@ccolor ).", MIN_CLOTH_COLOR, MAX_CLOTH_COLOR); clif_displaymessage(fd, output); @@ -1994,13 +1983,13 @@ int atcommand_dye(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_hair_style(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { int hair_style = 0; - if (!message || !*message || sscanf(message, "%d", &hair_style) < 1) + if (!extract(message, &hair_style)) { - std::string output = STRPRINTF( + FString output = STRPRINTF( "Please, enter a hair style (usage: @hairstyle/@hstyle ).", MIN_HAIR_STYLE, MAX_HAIR_STYLE); clif_displaymessage(fd, output); @@ -2028,13 +2017,13 @@ int atcommand_hair_style(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_hair_color(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { int hair_color = 0; - if (!message || !*message || sscanf(message, "%d", &hair_color) < 1) + if (!extract(message, &hair_color)) { - std::string output = STRPRINTF( + FString output = STRPRINTF( "Please, enter a hair color (usage: @haircolor/@hcolor ).", MIN_HAIR_COLOR, MAX_HAIR_COLOR); clif_displaymessage(fd, output); @@ -2062,9 +2051,9 @@ int atcommand_hair_color(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_spawn(const int fd, dumb_ptr sd, - const char *command, const char *message) + ZString message) { - char monster[100] {}; + MobName monster; int mob_id; int number = 0; int x = 0, y = 0; @@ -2072,8 +2061,7 @@ int atcommand_spawn(const int fd, dumb_ptr sd, int i, j, k; int mx, my, range; - if (!message || !*message - || sscanf(message, "%99s %d %d %d", monster, &number, &x, &y) < 1) + if (!extract(message, record<' ', 1>(&monster, &number, &x, &y))) { clif_displaymessage(fd, "Give a monster name/id please."); return -1; @@ -2081,7 +2069,7 @@ int atcommand_spawn(const int fd, dumb_ptr sd, // If monster identifier/name argument is a name if ((mob_id = mobdb_searchname(monster)) == 0) // check name first (to avoid possible name begining by a number) - mob_id = mobdb_checkid(atoi(monster)); + mob_id = mobdb_checkid(atoi(monster.c_str())); if (mob_id == 0) { @@ -2089,12 +2077,6 @@ int atcommand_spawn(const int fd, dumb_ptr sd, return -1; } - if (mob_id == 1288) - { - clif_displaymessage(fd, "Cannot spawn emperium."); - return -1; - } - if (number <= 0) number = 1; @@ -2104,8 +2086,8 @@ int atcommand_spawn(const int fd, dumb_ptr sd, number = battle_config.atc_spawn_quantity_limit; if (battle_config.etc_log) - PRINTF("%s monster='%s' id=%d count=%d (%d,%d)\n", command, monster, - mob_id, number, x, y); + PRINTF("@spawn monster='%s' id=%d count=%d (%d,%d)\n", + monster, mob_id, number, x, y); count = 0; range = sqrt(number) / 2; @@ -2125,7 +2107,7 @@ int atcommand_spawn(const int fd, dumb_ptr sd, my = sd->bl_y + random_::in(-range / 2, range / 2); else my = y; - k = mob_once_spawn(sd, "this", mx, my, "", mob_id, 1, ""); + k = mob_once_spawn(sd, MOB_THIS_MAP, mx, my, MobName(), mob_id, 1, NpcEvent()); } count += (k != 0) ? 1 : 0; } @@ -2135,7 +2117,7 @@ int atcommand_spawn(const int fd, dumb_ptr sd, clif_displaymessage(fd, "All monster summoned!"); else { - std::string output = STRPRINTF("%d monster(s) summoned!", + FString output = STRPRINTF("%d monster(s) summoned!", count); clif_displaymessage(fd, output); } @@ -2154,17 +2136,12 @@ int atcommand_spawn(const int fd, dumb_ptr sd, */ static void atcommand_killmonster_sub(const int fd, dumb_ptr sd, - const char *message, const int drop) + ZString message, const int drop) { - char map_name[100] {}; - map_local *map_id; - if (!message || !*message || sscanf(message, "%99s", map_name) < 1) - map_id = sd->bl_m; - else { - if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < 13) // 16 - 4 (.gat) - strcat(map_name, ".gat"); + MapName map_name; + extract(message, &map_name); map_id = map_mapname2mapid(map_name); if (map_id == nullptr) map_id = sd->bl_m; @@ -2177,8 +2154,6 @@ void atcommand_killmonster_sub(const int fd, dumb_ptr sd, BL::MOB); clif_displaymessage(fd, "All monsters killed!"); - - return; } /*========================================== @@ -2186,7 +2161,7 @@ void atcommand_killmonster_sub(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_killmonster(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { atcommand_killmonster_sub(fd, sd, message, 1); @@ -2202,7 +2177,7 @@ void atlist_nearby_sub(dumb_ptr bl, int fd) { nullpo_retv(bl); - std::string buf = STRPRINTF(" - \"%s\"", + FString buf = STRPRINTF(" - \"%s\"", bl->as_player()->status.name); clif_displaymessage(fd, buf); } @@ -2212,7 +2187,7 @@ void atlist_nearby_sub(dumb_ptr bl, int fd) *------------------------------------------ */ int atcommand_list_nearby(const int fd, dumb_ptr sd, - const char *, const char *) + ZString) { clif_displaymessage(fd, "Nearby players:"); map_foreachinarea(std::bind(atlist_nearby_sub, ph::_1, fd), @@ -2229,7 +2204,7 @@ int atcommand_list_nearby(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_killmonster2(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { atcommand_killmonster_sub(fd, sd, message, 0); @@ -2241,13 +2216,13 @@ int atcommand_killmonster2(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_gat(const int fd, dumb_ptr sd, - const char *, const char *) + ZString) { int y; for (y = 2; y >= -2; y--) { - std::string output = STRPRINTF( + FString output = STRPRINTF( "%s (x= %d, y= %d) %02X %02X %02X %02X %02X", sd->bl_m->name_, sd->bl_x - 2, sd->bl_y + y, map_getcell(sd->bl_m, sd->bl_x - 2, sd->bl_y + y), @@ -2266,18 +2241,19 @@ int atcommand_gat(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_packet(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { - int type = 0, flag = 0; + StatusChange type {}; + int flag = 0; - if (!message || !*message || sscanf(message, "%d %d", &type, &flag) < 2) + if (!extract(message, record<' '>(&type, &flag))) { clif_displaymessage(fd, - "Please, enter a status type/flag (usage: @packet )."); + "Please, enter a status type/flag (usage: @packet )."); return -1; } - clif_status_change(sd, StatusChange(type), flag); + clif_status_change(sd, type, flag); return 0; } @@ -2287,14 +2263,14 @@ int atcommand_packet(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_statuspoint(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { int point, new_status_point; - if (!message || !*message || (point = atoi(message)) == 0) + if (!extract(message, &point) || point == 0) { clif_displaymessage(fd, - "Please, enter a number (usage: @stpoint )."); + "Please, enter a number (usage: @stpoint )."); return -1; } @@ -2327,14 +2303,14 @@ int atcommand_statuspoint(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_skillpoint(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { int point, new_skill_point; - if (!message || !*message || (point = atoi(message)) == 0) + if (!extract(message, &point) || point == 0) { clif_displaymessage(fd, - "Please, enter a number (usage: @skpoint )."); + "Please, enter a number (usage: @skpoint )."); return -1; } @@ -2367,14 +2343,14 @@ int atcommand_skillpoint(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_zeny(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { int zeny, new_zeny; - if (!message || !*message || (zeny = atoi(message)) == 0) + if (!extract(message, &zeny) || zeny == 0) { clif_displaymessage(fd, - "Please, enter an amount (usage: @zeny )."); + "Please, enter an amount (usage: @zeny )."); return -1; } @@ -2408,14 +2384,13 @@ int atcommand_zeny(const int fd, dumb_ptr sd, */ template int atcommand_param(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { int value = 0, new_value; - if (!message || !*message || sscanf(message, "%d", &value) < 1 + if (!extract(message, &value) || value == 0) { - // there was a clang bug here // fortunately, STRPRINTF was not actually needed clif_displaymessage(fd, @@ -2455,11 +2430,11 @@ int atcommand_param(const int fd, dumb_ptr sd, */ //** Stat all by fritz (rewritten by [Yor]) int atcommand_all_stats(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { int count, value = 0, new_value; - if (!message || !*message || sscanf(message, "%d", &value) < 1 + if (!extract(message, &value) || value == 0) value = battle_config.max_parameter; @@ -2501,19 +2476,19 @@ int atcommand_all_stats(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_recall(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { - char character[100] {}; - dumb_ptr pl_sd; + CharName character; - if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) + if (!asplit(message, &character)) { clif_displaymessage(fd, - "Please, enter a player name (usage: @recall )."); + "Please, enter a player name (usage: @recall )."); return -1; } - if ((pl_sd = map_nick2sd(character)) != NULL) + dumb_ptr pl_sd = map_nick2sd(character); + if (pl_sd != NULL) { if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can recall only lower or same level @@ -2521,18 +2496,18 @@ int atcommand_recall(const int fd, dumb_ptr sd, && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { clif_displaymessage(fd, - "You are not authorised to warp somenone to your actual map."); + "You are not authorised to warp somenone to your actual map."); return -1; } if (pl_sd->bl_m && pl_sd->bl_m->flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { clif_displaymessage(fd, - "You are not authorised to warp this player from its actual map."); + "You are not authorised to warp this player from its actual map."); return -1; } pc_setpos(pl_sd, sd->mapname_, sd->bl_x, sd->bl_y, BeingRemoveWhy::QUIT); - std::string output = STRPRINTF("%s recalled!", character); + FString output = STRPRINTF("%s recalled!", character); clif_displaymessage(fd, output); } else @@ -2555,19 +2530,19 @@ int atcommand_recall(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_revive(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { - char character[100] {}; - dumb_ptr pl_sd; + CharName character; - if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) + if (!asplit(message, &character)) { clif_displaymessage(fd, - "Please, enter a player name (usage: @revive )."); + "Please, enter a player name (usage: @revive )."); return -1; } - if ((pl_sd = map_nick2sd(character)) != NULL) + dumb_ptr pl_sd = map_nick2sd(character); + if (pl_sd != NULL) { pl_sd->status.hp = pl_sd->status.max_hp; pc_setstand(pl_sd); @@ -2592,21 +2567,21 @@ int atcommand_revive(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_character_stats(const int fd, dumb_ptr, - const char *, const char *message) + ZString message) { - char character[100] {}; - dumb_ptr pl_sd; + CharName character; - if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) + if (!asplit(message, &character)) { clif_displaymessage(fd, - "Please, enter a player name (usage: @charstats )."); + "Please, enter a player name (usage: @charstats )."); return -1; } - if ((pl_sd = map_nick2sd(character)) != NULL) + dumb_ptr pl_sd = map_nick2sd(character); + if (pl_sd != NULL) { - std::string output; + FString output; output = STRPRINTF("'%s' stats:", pl_sd->status.name); clif_displaymessage(fd, output); output = STRPRINTF("Base Level - %d", pl_sd->status.base_level), @@ -2651,7 +2626,7 @@ int atcommand_character_stats(const int fd, dumb_ptr, */ //** Character Stats All by fritz int atcommand_character_stats_all(const int fd, dumb_ptr, - const char *, const char *) + ZString) { int count; @@ -2663,13 +2638,13 @@ int atcommand_character_stats_all(const int fd, dumb_ptr, dumb_ptr pl_sd = dumb_ptr(static_cast(session[i]->session_data.get())); if (pl_sd && pl_sd->state.auth) { - std::string gmlevel; + FString gmlevel; if (pc_isGM(pl_sd) > 0) gmlevel = STRPRINTF("| GM Lvl: %d", pc_isGM(pl_sd)); else gmlevel = " "; - std::string output; + FString output; output = STRPRINTF( "Name: %s | BLvl: %d | Job: Novice/Human (Lvl: %d) | HP: %d/%d | SP: %d/%d", pl_sd->status.name, pl_sd->status.base_level, @@ -2698,7 +2673,7 @@ int atcommand_character_stats_all(const int fd, dumb_ptr, clif_displaymessage(fd, "1 player found."); else { - std::string output = STRPRINTF("%d players found.", count); + FString output = STRPRINTF("%d players found.", count); clif_displaymessage(fd, output); } @@ -2710,26 +2685,21 @@ int atcommand_character_stats_all(const int fd, dumb_ptr, *------------------------------------------ */ int atcommand_character_option(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { - char character[100] {}; - int opt1_ = 0, opt2_ = 0, opt3_ = 0; - dumb_ptr pl_sd; - - if (!message || !*message - || sscanf(message, "%d %d %d %99[^\n]", &opt1_, &opt2_, &opt3_, - character) < 4 || opt1_ < 0 || opt2_ < 0 || opt3_ < 0) + Opt1 opt1; + Opt2 opt2; + Option opt3; + CharName character; + if (!asplit(message, &opt1, &opt2, &opt3, &character)) { clif_displaymessage(fd, - "Please, enter valid options and a player name (usage: @charoption )."); + "Please, enter valid options and a player name (usage: @charoption )."); return -1; } - Opt1 opt1 = Opt1(opt1_); - Opt2 opt2 = Opt2(opt2_); - Option opt3 = Option(opt3_); - - if ((pl_sd = map_nick2sd(character)) != NULL) + dumb_ptr pl_sd = map_nick2sd(character); + if (pl_sd != NULL) { if (pc_isGM(sd) >= pc_isGM(pl_sd)) { @@ -2762,31 +2732,19 @@ int atcommand_character_option(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_char_change_sex(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { - char character[100] {}; + CharName character; - if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) + if (!asplit(message, &character)) { clif_displaymessage(fd, - "Please, enter a player name (usage: @charchangesex )."); + "Please, enter a player name (usage: @charchangesex )."); return -1; } - // check player name - if (strlen(character) < 4) { - clif_displaymessage(fd, "Sorry, but a player name have at least 4 characters."); - return -1; - } - else if (strlen(character) > 23) - { - clif_displaymessage(fd, "Sorry, but a player name have 23 characters maximum."); - return -1; - } - else - { - chrif_char_ask_name(sd->status.account_id, character, 5, 0, 0, 0, 0, 0, 0); // type: 5 - changesex + chrif_char_ask_name(sd->status.account_id, character, 5, HumanTimeDiff()); // type: 5 - changesex clif_displaymessage(fd, "Character name sends to char-server to ask it."); } @@ -2799,31 +2757,19 @@ int atcommand_char_change_sex(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_char_block(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { - char character[100] {}; + CharName character; - if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) + if (!asplit(message, &character)) { clif_displaymessage(fd, - "Please, enter a player name (usage: @block )."); + "Please, enter a player name (usage: @block )."); return -1; } - // check player name - if (strlen(character) < 4) { - clif_displaymessage(fd, "Sorry, but a player name have at least 4 characters."); - return -1; - } - else if (strlen(character) > 23) - { - clif_displaymessage(fd, "Sorry, but a player name have 23 characters maximum."); - return -1; - } - else - { - chrif_char_ask_name(sd->status.account_id, character, 1, 0, 0, 0, 0, 0, 0); // type: 1 - block + chrif_char_ask_name(sd->status.account_id, character, 1, HumanTimeDiff()); // type: 1 - block clif_displaymessage(fd, "Character name sends to char-server to ask it."); } @@ -2847,94 +2793,21 @@ int atcommand_char_block(const int fd, dumb_ptr sd, *------------------------------------------ */ int atcommand_char_ban(const int fd, dumb_ptr sd, - const char *, const char *message) + ZString message) { - char modif[100] {}; - char character[100] {}; - char *modif_p; - int year, month, day, hour, minute, second, value; + HumanTimeDiff modif; + CharName character; - if (!message || !*message - || sscanf(message, "%s %99[^\n]", modif, character) < 2) + if (!asplit(message, &modif, &character) + || !modif) { clif_displaymessage(fd, - "Please, enter ban time and a player name (usage: @charban/@ban/@banish/@charbanish