diff --git a/CMakeLists.txt b/CMakeLists.txt index b14e2cf1f..19e49c9c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,9 +13,13 @@ include(MSVC) set (HAVE_STRDUP 0) set (HAVE_STRLCAT 0) set (HAVE_LIBBSD 0) +set (HAVE_SIGNAL_H 0) +set (HAVE_EXECINFO_H 0) else (MSVC) INCLUDE (CheckIncludeFile) +CHECK_INCLUDE_FILE(signal.h HAVE_SIGNAL_H) +CHECK_INCLUDE_FILE(execinfo.h HAVE_EXECINFO_H) CHECK_INCLUDE_FILE(bsd/string.h HAVE_LIBBSD) INCLUDE (CheckFunctionExists) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 653e036bd..bdf0465d2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -261,6 +261,10 @@ add_test(server test_eressea) install(TARGETS eressea DESTINATION "bin") +if (HAVE_EXECINFO_H AND HAVE_SIGNAL_H) + add_definitions(-DHAVE_BACKTRACE) +endif () + if (HAVE_LIBBSD) add_definitions(-DHAVE_LIBBSD) endif (HAVE_LIBBSD) diff --git a/src/kernel/order.c b/src/kernel/order.c index 7099dab19..b7da372c1 100644 --- a/src/kernel/order.c +++ b/src/kernel/order.c @@ -67,8 +67,6 @@ keyword_t getkeyword(const order * ord) * keywords are expanded to their full length. */ char* get_command(const order *ord, const struct locale *lang, char *sbuffer, size_t size) { - order_data *od = NULL; - const char * text; keyword_t kwd = ORD_KEYWORD(ord); sbstring sbs; @@ -80,28 +78,35 @@ char* get_command(const order *ord, const struct locale *lang, char *sbuffer, si sbs_strcat(&sbs, "@"); } - if (ord->id < 0) { - skill_t sk = (skill_t)(100+ord->id); - assert(kwd == K_STUDY && sk != SK_MAGIC && sk < MAXSKILLS); - text = skillname(sk, lang); - } else { - od = odata_load(ord->id); - text = OD_STRING(od); - } if (kwd != NOKEYWORD) { const char *str = (const char *)LOC(lang, keyword(kwd)); assert(str); sbs_strcat(&sbs, str); - if (text) { - sbs_strcat(&sbs, " "); + if (ord->id < 0) { + skill_t sk = (skill_t)(100+ord->id); + assert(kwd == K_STUDY && sk != SK_MAGIC && sk < MAXSKILLS); + str = skillname(sk, lang); + if (str) { + if (strchr(str, ' ') == NULL) { + sbs_strcat(&sbs, " "); + sbs_strcat(&sbs, str); + } + else { + sbs_strcat(&sbs, " '"); + sbs_strcat(&sbs, str); + sbs_strcat(&sbs, "'"); + } + } + } else { + order_data *od = odata_load(ord->id); + str = OD_STRING(od); + if (str) { + sbs_strcat(&sbs, " "); + sbs_strcat(&sbs, str); + } + odata_release(od); } } - if (text) { - sbs_strcat(&sbs, text); - } - if (od) { - odata_release(od); - } return sbuffer; } @@ -118,30 +123,38 @@ int stream_order(struct stream *out, const struct order *ord, const struct local swrite("@", 1, 1, out); } - if (ord->id < 0) { - skill_t sk = (skill_t)(100 + ord->id); - assert(kwd == K_STUDY && sk != SK_MAGIC && sk < MAXSKILLS); - text = skillname(sk, lang); - } - else { - od = odata_load(ord->id); - text = OD_STRING(od); - } if (kwd != NOKEYWORD) { const char *str = (const char *)LOC(lang, keyword(kwd)); assert(str); swrite(str, 1, strlen(str), out); } - if (text) { - char obuf[1024]; - swrite(" ", 1, 1, out); - if (escape) { - text = str_escape(text, obuf, sizeof(obuf)); + if (ord->id < 0) { + skill_t sk = (skill_t)(100 + ord->id); + + assert(kwd == K_STUDY && sk != SK_MAGIC && sk < MAXSKILLS); + text = skillname(sk, lang); + if (strchr(text, ' ') != NULL) { + swrite(" '", 1, 2, out); + swrite(text, 1, strlen(text), out); + swrite("'", 1, 1, out); + } + else { + swrite(" ", 1, 1, out); + swrite(text, 1, strlen(text), out); } - swrite(text, 1, strlen(text), out); } - if (od) { + else { + od = odata_load(ord->id); + text = OD_STRING(od); + if (text) { + char obuf[1024]; + swrite(" ", 1, 1, out); + if (escape) { + text = str_escape(text, obuf, sizeof(obuf)); + } + swrite(text, 1, strlen(text), out); + } odata_release(od); } @@ -514,11 +527,27 @@ keyword_t init_order(const struct order *ord, const struct locale *lang) parser_od = NULL; } if (ord->id < 0) { + const char *str; skill_t sk = (skill_t)(100 + ord->id); + assert(sk < MAXSKILLS); assert(lang); assert(kwd == K_STUDY); - init_tokens_str(skillname(sk, lang)); + str = skillname(sk, lang); + if (strchr(str, ' ') == NULL) { + init_tokens_str(str); + } + else { + char token[32], *dup; + size_t len = strlen(str); + assert(len + 3 < sizeof(token)); + token[0] = '\''; + memcpy(token + 1, str, len); + token[len + 1] = '\''; + token[len + 2] = '\0'; + dup = str_strdup(token); + init_tokens_ex(dup, dup, free); + } } else { const char *str; diff --git a/src/kernel/order.test.c b/src/kernel/order.test.c index 74679db6e..acba56f0f 100644 --- a/src/kernel/order.test.c +++ b/src/kernel/order.test.c @@ -3,10 +3,14 @@ #include "order.h" #include +#include #include #include +#include +#include + #include #include #include @@ -331,11 +335,73 @@ static void test_study_orders(CuTest *tc) { test_teardown(); } +static void test_study_order(CuTest *tc) { + char token[32]; + stream out; + unit *u; + struct locale *lang; + + test_setup(); + lang = get_or_create_locale("de"); + locale_setstring(lang, mkname("skill", skillnames[SK_ALCHEMY]), "Alchemie"); + locale_setstring(lang, "keyword::study", "LERNE"); + init_keywords(lang); + init_skills(lang); + u = test_create_unit(test_create_faction(NULL), test_create_plain(0, 0)); + u->thisorder = create_order(K_STUDY, lang, "ALCH"); + CuAssertIntEquals(tc, K_STUDY, init_order(u->thisorder, lang)); + CuAssertStrEquals(tc, skillname(SK_ALCHEMY, lang), gettoken(token, sizeof(token))); + + CuAssertStrEquals(tc, "LERNE Alchemie", get_command(u->thisorder, lang, token, sizeof(token))); + + mstream_init(&out); + stream_order(&out, u->thisorder, lang, true); + swrite("\n", 1, 1, &out); + out.api->rewind(out.handle); + out.api->readln(out.handle, token, sizeof(token)); + CuAssertStrEquals(tc, "LERNE Alchemie", token); + mstream_done(&out); + + test_teardown(); +} + +static void test_study_order_quoted(CuTest *tc) { + char token[32]; + stream out; + unit *u; + struct locale *lang; + + test_setup(); + lang = get_or_create_locale("de"); + locale_setstring(lang, mkname("skill", skillnames[SK_WEAPONLESS]), "Waffenloser Kampf"); + locale_setstring(lang, "keyword::study", "LERNE"); + init_keywords(lang); + init_skills(lang); + u = test_create_unit(test_create_faction(NULL), test_create_plain(0, 0)); + u->thisorder = create_order(K_STUDY, lang, "Waffenloser~Kampf"); + CuAssertIntEquals(tc, K_STUDY, init_order(u->thisorder, lang)); + CuAssertStrEquals(tc, skillname(SK_WEAPONLESS, lang), gettoken(token, sizeof(token))); + + CuAssertStrEquals(tc, "LERNE 'Waffenloser Kampf'", get_command(u->thisorder, lang, token, sizeof(token))); + + mstream_init(&out); + stream_order(&out, u->thisorder, lang, true); + swrite("\n", 1, 1, &out); + out.api->rewind(out.handle); + out.api->readln(out.handle, token, sizeof(token)); + CuAssertStrEquals(tc, "LERNE 'Waffenloser Kampf'", token); + mstream_done(&out); + + test_teardown(); +} + CuSuite *get_order_suite(void) { CuSuite *suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_create_order); SUITE_ADD_TEST(suite, test_study_orders); + SUITE_ADD_TEST(suite, test_study_order); + SUITE_ADD_TEST(suite, test_study_order_quoted); SUITE_ADD_TEST(suite, test_parse_order); SUITE_ADD_TEST(suite, test_parse_make); SUITE_ADD_TEST(suite, test_parse_make_temp); diff --git a/src/kernel/save.c b/src/kernel/save.c index fe1bb525a..858cd7040 100644 --- a/src/kernel/save.c +++ b/src/kernel/save.c @@ -1418,7 +1418,7 @@ int read_game(gamedata *data) read_attribs(data, &global.attribs, NULL); READ_INT(store, &turn); log_debug(" - reading turn %d", turn); - rng_init(turn); + rng_init(turn + config_get_int("game.seed", 0)); READ_INT(store, NULL); /* max_unique_id = ignore */ READ_INT(store, &nextborder); diff --git a/src/laws.c b/src/laws.c index f4f0f24ee..cf8f2668f 100644 --- a/src/laws.c +++ b/src/laws.c @@ -141,7 +141,15 @@ bool IsImmune(const faction * f) int NMRTimeout(void) { - return config_get_int("nmr.timeout", 0); + int nmr_timeout = config_get_int("nmr.timeout", 0); + int ini_timeout = config_get_int("game.maxnmr", 0); + if (nmr_timeout > 0) { + if (ini_timeout > nmr_timeout) { + return nmr_timeout; + } + return (ini_timeout > 0) ? ini_timeout : nmr_timeout; + } + return ini_timeout; } bool LongHunger(const struct unit *u) diff --git a/src/laws.test.c b/src/laws.test.c index edfdd6f4c..f9b4cfcf3 100644 --- a/src/laws.test.c +++ b/src/laws.test.c @@ -1745,6 +1745,20 @@ static void test_cansee_sphere(CuTest *tc) { test_teardown(); } +static void test_nmr_timeout(CuTest *tc) { + test_setup(); + CuAssertIntEquals(tc, 0, NMRTimeout()); + config_set_int("nmr.timeout", 5); + CuAssertIntEquals(tc, 5, NMRTimeout()); + config_set_int("game.maxnmr", 4); + CuAssertIntEquals(tc, 4, NMRTimeout()); + config_set("nmr.timeout", NULL); + CuAssertIntEquals(tc, 4, NMRTimeout()); + config_set("game.maxnmr", NULL); + CuAssertIntEquals(tc, 0, NMRTimeout()); + test_teardown(); +} + CuSuite *get_laws_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -1816,6 +1830,7 @@ CuSuite *get_laws_suite(void) SUITE_ADD_TEST(suite, test_cansee); SUITE_ADD_TEST(suite, test_cansee_ring); SUITE_ADD_TEST(suite, test_cansee_sphere); + SUITE_ADD_TEST(suite, test_nmr_timeout); return suite; } diff --git a/src/main.c b/src/main.c index 489d0d868..bb353fc2f 100644 --- a/src/main.c +++ b/src/main.c @@ -83,8 +83,10 @@ static void load_inifile(void) static const char * valid_keys[] = { "game.id", "game.deadlog", + "game.maxnmr", "game.name", "game.start", + "game.seed", "game.locale", "game.verbose", "game.report", @@ -257,7 +259,7 @@ static int parse_args(int argc, char **argv) return 0; } -#if defined(HAVE_SIGACTION) && defined(HAVE_EXECINFO) +#ifdef HAVE_BACKTRACE #include #include @@ -278,7 +280,7 @@ static int setup_signal_handler(void) { struct sigaction act; - act.sa_flags = SA_ONESHOT | SA_SIGINFO; + act.sa_flags = SA_RESETHAND | SA_SIGINFO; act.sa_sigaction = report_segfault; sigfillset(&act.sa_mask); return sigaction(SIGSEGV, &act, NULL); diff --git a/storage b/storage index d807ef5ce..5623ee652 160000 --- a/storage +++ b/storage @@ -1 +1 @@ -Subproject commit d807ef5ce64b3425b31fb440e0b93a4d233f517a +Subproject commit 5623ee6527e97af20c7d8efc03800b6fe1579744