From f3515c8e07d47f75f9f42ae9ca92961868b4e46f Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 7 Oct 2017 18:03:22 +0200 Subject: [PATCH 1/4] move read_orders to its own module --- src/CMakeLists.txt | 1 + src/orderfile.c | 30 ++++++++++++++++++++++++++++++ src/orderfile.h | 18 ++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 src/orderfile.c create mode 100644 src/orderfile.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c4c306118..2af57d6b7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -116,6 +116,7 @@ set (ERESSEA_SRC magic.c market.c morale.c + orderfile.c randenc.c renumber.c volcano.c diff --git a/src/orderfile.c b/src/orderfile.c new file mode 100644 index 000000000..9f044ddbe --- /dev/null +++ b/src/orderfile.c @@ -0,0 +1,30 @@ +#include "orderfile.h" + +#include +#include + +#include +#include + +#include +void read_orders(stream *strm) +{ + faction *f = NULL; + unit *u = NULL; + char line[1024]; + + line = strm->api->readln(strm->handle, line, sizeof(line)); +} + +void read_orderfile(const char *filename) +{ + stream strm; + FILE * F = fopen(filename, "r"); + if (!F) { + return; + } + fstream_init(&strm, F); + read_orders(&strm); + fstream_done(&strm); + fclose(F); +} diff --git a/src/orderfile.h b/src/orderfile.h new file mode 100644 index 000000000..11364d361 --- /dev/null +++ b/src/orderfile.h @@ -0,0 +1,18 @@ +#ifndef H_ORDERFILE +#define H_ORDERFILE + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + struct stream; + + void read_orderfile(const char *filename); + void read_orders(struct stream *strm); + +#ifdef __cplusplus +} +#endif +#endif From c2634bd095074266d8a664f042c76f164d3a2444 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 7 Oct 2017 19:44:23 +0200 Subject: [PATCH 2/4] move readorders to orderfile module. --- src/bind_eressea.c | 1 + src/kernel/save.c | 211 ---------------------------------------- src/kernel/save.h | 1 - src/orderfile.c | 235 +++++++++++++++++++++++++++++++++++++++++---- src/orderfile.h | 5 +- 5 files changed, 220 insertions(+), 233 deletions(-) diff --git a/src/bind_eressea.c b/src/bind_eressea.c index de8751306..ffc23fbfc 100755 --- a/src/bind_eressea.c +++ b/src/bind_eressea.c @@ -3,6 +3,7 @@ #include #include "json.h" +#include "orderfile.h" #include #include diff --git a/src/kernel/save.c b/src/kernel/save.c index 6338b6827..aa09a27be 100644 --- a/src/kernel/save.c +++ b/src/kernel/save.c @@ -123,217 +123,6 @@ char *rns(FILE * f, char *c, size_t size) return c; } - -static unit *unitorders(FILE * F, int enc, struct faction *f) -{ - int i; - unit *u; - - if (!f) - return NULL; - - i = getid(); - u = findunitg(i, NULL); - - if (u && u->faction == f) { - order **ordp; - - if (!fval(u, UFL_ORDERS)) { - /* alle wiederholbaren, langen befehle werden gesichert: */ - fset(u, UFL_ORDERS); - u->old_orders = u->orders; - ordp = &u->old_orders; - while (*ordp) { - order *ord = *ordp; - keyword_t kwd = getkeyword(ord); - if (!is_repeated(kwd)) { - *ordp = ord->next; - ord->next = NULL; - free_order(ord); - } - else { - ordp = &ord->next; - } - } - } - else { - free_orders(&u->orders); - } - u->orders = 0; - - ordp = &u->orders; - - for (;;) { - const char *s; - /* Erst wenn wir sicher sind, dass kein Befehl - * eingegeben wurde, checken wir, ob nun eine neue - * Einheit oder ein neuer Spieler drankommt */ - - s = getbuf(F, enc); - if (s == NULL) - break; - - if (s[0]) { - if (s[0] != '@') { - char token[64]; - const char *stok = s; - stok = parse_token(&stok, token, sizeof(token)); - - if (stok) { - bool quit = false; - param_t param = findparam(stok, u->faction->locale); - switch (param) { - case P_UNIT: - case P_REGION: - quit = true; - break; - case P_FACTION: - case P_NEXT: - case P_GAMENAME: - /* these terminate the orders, so we apply extra checking */ - if (strlen(stok) >= 3) { - quit = true; - break; - } - else { - quit = false; - } - break; - default: - break; - } - if (quit) { - break; - } - } - } - /* Nun wird der Befehl erzeut und eingehängt */ - *ordp = parse_order(s, u->faction->locale); - if (*ordp) { - ordp = &(*ordp)->next; - } - else { - ADDMSG(&f->msgs, msg_message("parse_error", "unit command", u, s)); - } - } - } - - } - else { - return NULL; - } - return u; -} - -static faction *factionorders(void) -{ - faction *f = NULL; - int fid = getid(); - - f = findfaction(fid); - - if (f != NULL && !fval(f, FFL_NPC)) { - char token[128]; - const char *pass = gettoken(token, sizeof(token)); - - if (!checkpasswd(f, (const char *)pass)) { - log_debug("Invalid password for faction %s", itoa36(fid)); - ADDMSG(&f->msgs, msg_message("wrongpasswd", "password", pass)); - return 0; - } - /* Die Partei hat sich zumindest gemeldet, so dass sie noch - * nicht als untätig gilt */ - f->lastorders = turn; - - } - else { - log_debug("orders for invalid faction %s", itoa36(fid)); - } - return f; -} - -int readorders(const char *filename) -{ - FILE *F = NULL; - const char *b; - int nfactions = 0; - struct faction *f = NULL; - - F = fopen(filename, "r"); - if (!F) { - perror(filename); - return -1; - } - log_info("reading orders from %s", filename); - - /* TODO: recognize UTF8 BOM */ - b = getbuf(F, enc_gamedata); - - /* Auffinden der ersten Partei, und danach abarbeiten bis zur letzten - * Partei */ - - while (b) { - char token[128]; - const struct locale *lang = f ? f->locale : default_locale; - param_t p; - const char *s; - init_tokens_str(b); - s = gettoken(token, sizeof(token)); - p = findparam_block(s, lang, true); - switch (p) { - case P_GAMENAME: - case P_FACTION: - f = factionorders(); - if (f) { - ++nfactions; - } - - b = getbuf(F, enc_gamedata); - break; - - /* in factionorders wird nur eine zeile gelesen: - * diejenige mit dem passwort. Die befehle der units - * werden geloescht, und die Partei wird als aktiv - * vermerkt. */ - - case P_UNIT: - if (!f || !unitorders(F, enc_gamedata, f)) { - do { - b = getbuf(F, enc_gamedata); - if (!b) { - break; - } - init_tokens_str(b); - s = gettoken(token, sizeof(token)); - p = (s && s[0] != '@') ? findparam(s, lang) : NOPARAM; - } while ((p != P_UNIT || !f) && p != P_FACTION && p != P_NEXT - && p != P_GAMENAME); - } - break; - - /* Falls in unitorders() abgebrochen wird, steht dort entweder eine neue - * Partei, eine neue Einheit oder das File-Ende. Das switch() wird erneut - * durchlaufen, und die entsprechende Funktion aufgerufen. Man darf buf - * auf alle Fälle nicht überschreiben! Bei allen anderen Einträgen hier - * muss buf erneut gefüllt werden, da die betreffende Information in nur - * einer Zeile steht, und nun die nächste gelesen werden muss. */ - - case P_NEXT: - f = NULL; - b = getbuf(F, enc_gamedata); - break; - - default: - b = getbuf(F, enc_gamedata); - break; - } - } - - fclose(F); - log_info("done reading orders for %d factions", nfactions); - return 0; -} - /* ------------------------------------------------------------- */ /* #define INNER_WORLD */ diff --git a/src/kernel/save.h b/src/kernel/save.h index 763ff66b6..e8b631aab 100644 --- a/src/kernel/save.h +++ b/src/kernel/save.h @@ -43,7 +43,6 @@ extern "C" { /* TODO: is this *really* still in use? */ extern int enc_gamedata; - int readorders(const char *filename); int readgame(const char *filename); int writegame(const char *filename); diff --git a/src/orderfile.c b/src/orderfile.c index 9f044ddbe..90348cc17 100644 --- a/src/orderfile.c +++ b/src/orderfile.c @@ -1,30 +1,231 @@ +#include +#include #include "orderfile.h" #include #include +#include +#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include -void read_orders(stream *strm) +#include + + +static unit *unitorders(FILE * F, int enc, struct faction *f) +{ + int i; + unit *u; + + if (!f) + return NULL; + + i = getid(); + u = findunitg(i, NULL); + + if (u && u->faction == f) { + order **ordp; + + if (!fval(u, UFL_ORDERS)) { + /* alle wiederholbaren, langen befehle werden gesichert: */ + fset(u, UFL_ORDERS); + u->old_orders = u->orders; + ordp = &u->old_orders; + while (*ordp) { + order *ord = *ordp; + keyword_t kwd = getkeyword(ord); + if (!is_repeated(kwd)) { + *ordp = ord->next; + ord->next = NULL; + free_order(ord); + } + else { + ordp = &ord->next; + } + } + } + else { + free_orders(&u->orders); + } + u->orders = 0; + + ordp = &u->orders; + + for (;;) { + const char *s; + /* Erst wenn wir sicher sind, dass kein Befehl + * eingegeben wurde, checken wir, ob nun eine neue + * Einheit oder ein neuer Spieler drankommt */ + + s = getbuf(F, enc); + if (s == NULL) + break; + + if (s[0]) { + if (s[0] != '@') { + char token[64]; + const char *stok = s; + stok = parse_token(&stok, token, sizeof(token)); + + if (stok) { + bool quit = false; + param_t param = findparam(stok, u->faction->locale); + switch (param) { + case P_UNIT: + case P_REGION: + quit = true; + break; + case P_FACTION: + case P_NEXT: + case P_GAMENAME: + /* these terminate the orders, so we apply extra checking */ + if (strlen(stok) >= 3) { + quit = true; + break; + } + else { + quit = false; + } + break; + default: + break; + } + if (quit) { + break; + } + } + } + /* Nun wird der Befehl erzeut und eingehängt */ + *ordp = parse_order(s, u->faction->locale); + if (*ordp) { + ordp = &(*ordp)->next; + } + else { + ADDMSG(&f->msgs, msg_message("parse_error", "unit command", u, s)); + } + } + } + + } + else { + return NULL; + } + return u; +} + +static faction *factionorders(void) { faction *f = NULL; - unit *u = NULL; - char line[1024]; + int fid = getid(); - line = strm->api->readln(strm->handle, line, sizeof(line)); -} + f = findfaction(fid); + + if (f != NULL && !fval(f, FFL_NPC)) { + char token[128]; + const char *pass = gettoken(token, sizeof(token)); + + if (!checkpasswd(f, (const char *)pass)) { + log_debug("Invalid password for faction %s", itoa36(fid)); + ADDMSG(&f->msgs, msg_message("wrongpasswd", "password", pass)); + return 0; + } + /* Die Partei hat sich zumindest gemeldet, so dass sie noch + * nicht als untätig gilt */ + f->lastorders = turn; -void read_orderfile(const char *filename) -{ - stream strm; - FILE * F = fopen(filename, "r"); - if (!F) { - return; } - fstream_init(&strm, F); - read_orders(&strm); - fstream_done(&strm); - fclose(F); + else { + log_debug("orders for invalid faction %s", itoa36(fid)); + } + return f; +} + +int readorders(const char *filename) +{ + FILE *F = NULL; + const char *b; + int nfactions = 0; + int enc_gamedata = ENCODING_UTF8; + struct faction *f = NULL; + + F = fopen(filename, "r"); + if (!F) { + perror(filename); + return -1; + } + log_info("reading orders from %s", filename); + + /* TODO: recognize UTF8 BOM */ + b = getbuf(F, enc_gamedata); + + /* Auffinden der ersten Partei, und danach abarbeiten bis zur letzten + * Partei */ + + while (b) { + char token[128]; + const struct locale *lang = f ? f->locale : default_locale; + param_t p; + const char *s; + init_tokens_str(b); + s = gettoken(token, sizeof(token)); + p = findparam_block(s, lang, true); + switch (p) { + case P_GAMENAME: + case P_FACTION: + f = factionorders(); + if (f) { + ++nfactions; + } + + b = getbuf(F, enc_gamedata); + break; + + /* in factionorders wird nur eine zeile gelesen: + * diejenige mit dem passwort. Die befehle der units + * werden geloescht, und die Partei wird als aktiv + * vermerkt. */ + + case P_UNIT: + if (!f || !unitorders(F, enc_gamedata, f)) { + do { + b = getbuf(F, enc_gamedata); + if (!b) { + break; + } + init_tokens_str(b); + s = gettoken(token, sizeof(token)); + p = (s && s[0] != '@') ? findparam(s, lang) : NOPARAM; + } while ((p != P_UNIT || !f) && p != P_FACTION && p != P_NEXT + && p != P_GAMENAME); + } + break; + + /* Falls in unitorders() abgebrochen wird, steht dort entweder eine neue + * Partei, eine neue Einheit oder das File-Ende. Das switch() wird erneut + * durchlaufen, und die entsprechende Funktion aufgerufen. Man darf buf + * auf alle Fälle nicht überschreiben! Bei allen anderen Einträgen hier + * muss buf erneut gefüllt werden, da die betreffende Information in nur + * einer Zeile steht, und nun die nächste gelesen werden muss. */ + + case P_NEXT: + f = NULL; + b = getbuf(F, enc_gamedata); + break; + + default: + b = getbuf(F, enc_gamedata); + break; + } + } + + fclose(F); + log_info("done reading orders for %d factions", nfactions); + return 0; } diff --git a/src/orderfile.h b/src/orderfile.h index 11364d361..b36b8acae 100644 --- a/src/orderfile.h +++ b/src/orderfile.h @@ -7,10 +7,7 @@ extern "C" { #endif - struct stream; - - void read_orderfile(const char *filename); - void read_orders(struct stream *strm); + int readorders(const char *filename); #ifdef __cplusplus } From 703c6c038531216d7fe3ba4a59ff58f24b7ce3aa Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 7 Oct 2017 20:17:04 +0200 Subject: [PATCH 3/4] some examples for how the new orderfile logic can be used to write unit tests. --- src/CMakeLists.txt | 1 + src/orderfile.c | 54 ++++++++++++++++---------- src/orderfile.h | 6 +++ src/orderfile.test.c | 91 ++++++++++++++++++++++++++++++++++++++++++++ src/test_eressea.c | 1 + 5 files changed, 133 insertions(+), 20 deletions(-) create mode 100644 src/orderfile.test.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2af57d6b7..e5ae8de7a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -218,6 +218,7 @@ set(TESTS_SRC monsters.test.c move.test.c names.test.c + orderfile.test.c piracy.test.c prefix.test.c renumber.test.c diff --git a/src/orderfile.c b/src/orderfile.c index 90348cc17..0db6a0cba 100644 --- a/src/orderfile.c +++ b/src/orderfile.c @@ -18,8 +18,7 @@ #include #include - -static unit *unitorders(FILE * F, int enc, struct faction *f) +static unit *unitorders(input *in, faction *f) { int i; unit *u; @@ -64,7 +63,7 @@ static unit *unitorders(FILE * F, int enc, struct faction *f) * eingegeben wurde, checken wir, ob nun eine neue * Einheit oder ein neuer Spieler drankommt */ - s = getbuf(F, enc); + s = in->getbuf(in->data); if (s == NULL) break; @@ -147,23 +146,14 @@ static faction *factionorders(void) return f; } -int readorders(const char *filename) +int read_orders(input *in) { - FILE *F = NULL; const char *b; int nfactions = 0; - int enc_gamedata = ENCODING_UTF8; struct faction *f = NULL; - F = fopen(filename, "r"); - if (!F) { - perror(filename); - return -1; - } - log_info("reading orders from %s", filename); - /* TODO: recognize UTF8 BOM */ - b = getbuf(F, enc_gamedata); + b = in->getbuf(in->data); /* Auffinden der ersten Partei, und danach abarbeiten bis zur letzten * Partei */ @@ -184,7 +174,7 @@ int readorders(const char *filename) ++nfactions; } - b = getbuf(F, enc_gamedata); + b = in->getbuf(in->data); break; /* in factionorders wird nur eine zeile gelesen: @@ -193,9 +183,9 @@ int readorders(const char *filename) * vermerkt. */ case P_UNIT: - if (!f || !unitorders(F, enc_gamedata, f)) { + if (!f || !unitorders(in, f)) { do { - b = getbuf(F, enc_gamedata); + b = in->getbuf(in->data); if (!b) { break; } @@ -216,16 +206,40 @@ int readorders(const char *filename) case P_NEXT: f = NULL; - b = getbuf(F, enc_gamedata); + b = in->getbuf(in->data); break; default: - b = getbuf(F, enc_gamedata); + b = in->getbuf(in->data); break; } } - fclose(F); log_info("done reading orders for %d factions", nfactions); return 0; } + +static const char * file_getbuf(void *data) +{ + FILE *F = (FILE *)data; + return getbuf(F, ENCODING_UTF8); +} + +int readorders(const char *filename) +{ + input in; + int result; + + FILE *F = NULL; + F = fopen(filename, "r"); + if (!F) { + perror(filename); + return -1; + } + log_info("reading orders from %s", filename); + in.getbuf = file_getbuf; + in.data = F; + result = read_orders(&in); + fclose(F); + return result; +} diff --git a/src/orderfile.h b/src/orderfile.h index b36b8acae..43b86042e 100644 --- a/src/orderfile.h +++ b/src/orderfile.h @@ -7,6 +7,12 @@ extern "C" { #endif + typedef struct input { + const char *(*getbuf)(void *data); + void *data; + } input; + + int read_orders(struct input *in); int readorders(const char *filename); #ifdef __cplusplus diff --git a/src/orderfile.test.c b/src/orderfile.test.c new file mode 100644 index 000000000..87992f584 --- /dev/null +++ b/src/orderfile.test.c @@ -0,0 +1,91 @@ +#include +#include + +#include "orderfile.h" + +#include + +#include +#include + +static const char *getbuf_null(void *data) +{ + return NULL; +} + +static void test_read_orders(CuTest *tc) { + input in; + test_setup(); + in.getbuf = getbuf_null; + in.data = NULL; + CuAssertIntEquals(tc, 0, read_orders(&in)); + test_cleanup(); +} + +typedef struct order_list { + const char *orders[8]; + int next; +} order_list; + +static const char *getbuf_list(void *data) +{ + order_list * olist = (order_list *)data; + if (olist->next >= 8) { + return NULL; + } + return olist->orders[olist->next++]; +} + +static void test_faction_password_okay(CuTest *tc) { + input in; + faction *f; + order_list olist; + + test_setup(); + f = test_create_faction(NULL); + renumber_faction(f, 1); + CuAssertIntEquals(tc, 1, f->no); + faction_setpassword(f, "password"); + f->lastorders = turn - 1; + olist.orders[0] = "ERESSEA 1 password"; + olist.orders[1] = NULL; + olist.next = 0; + in.getbuf = getbuf_list; + in.data = &olist; + CuAssertIntEquals(tc, 0, read_orders(&in)); + CuAssertIntEquals(tc, 2, olist.next); + CuAssertIntEquals(tc, turn, f->lastorders); + test_cleanup(); +} + +static void test_faction_password_bad(CuTest *tc) { + input in; + faction *f; + order_list olist; + + test_setup(); + f = test_create_faction(NULL); + renumber_faction(f, 1); + CuAssertIntEquals(tc, 1, f->no); + faction_setpassword(f, "password"); + f->lastorders = turn - 1; + olist.orders[0] = "ERESSEA 1 patzword"; + olist.orders[1] = NULL; + olist.next = 0; + in.getbuf = getbuf_list; + in.data = &olist; + CuAssertIntEquals(tc, 0, read_orders(&in)); + CuAssertIntEquals(tc, 2, olist.next); + CuAssertIntEquals(tc, turn - 1, f->lastorders); + test_cleanup(); +} + +CuSuite *get_orderfile_suite(void) +{ + CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_read_orders); + SUITE_ADD_TEST(suite, test_faction_password_okay); + SUITE_ADD_TEST(suite, test_faction_password_bad); + + return suite; +} diff --git a/src/test_eressea.c b/src/test_eressea.c index 215bdd2c5..38c5cf026 100644 --- a/src/test_eressea.c +++ b/src/test_eressea.c @@ -132,6 +132,7 @@ int RunAllTests(int argc, char *argv[]) ADD_SUITE(monsters); ADD_SUITE(move); ADD_SUITE(names); + ADD_SUITE(orderfile); ADD_SUITE(otherfaction); ADD_SUITE(piracy); ADD_SUITE(prefix); From e87a26d96128a6a4a822a9c9a5ec477090be6933 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 7 Oct 2017 20:25:07 +0200 Subject: [PATCH 4/4] Slightly reduce amount of code required for a test. --- src/orderfile.test.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/orderfile.test.c b/src/orderfile.test.c index 87992f584..10658a644 100644 --- a/src/orderfile.test.c +++ b/src/orderfile.test.c @@ -23,16 +23,13 @@ static void test_read_orders(CuTest *tc) { } typedef struct order_list { - const char *orders[8]; + const char **orders; int next; } order_list; static const char *getbuf_list(void *data) { order_list * olist = (order_list *)data; - if (olist->next >= 8) { - return NULL; - } return olist->orders[olist->next++]; } @@ -40,6 +37,7 @@ static void test_faction_password_okay(CuTest *tc) { input in; faction *f; order_list olist; + const char *orders[] = { "ERESSEA 1 password", NULL }; test_setup(); f = test_create_faction(NULL); @@ -47,8 +45,7 @@ static void test_faction_password_okay(CuTest *tc) { CuAssertIntEquals(tc, 1, f->no); faction_setpassword(f, "password"); f->lastorders = turn - 1; - olist.orders[0] = "ERESSEA 1 password"; - olist.orders[1] = NULL; + olist.orders = orders; olist.next = 0; in.getbuf = getbuf_list; in.data = &olist; @@ -62,15 +59,15 @@ static void test_faction_password_bad(CuTest *tc) { input in; faction *f; order_list olist; + const char *orders[] = { "ERESSEA 1 password", NULL }; test_setup(); f = test_create_faction(NULL); renumber_faction(f, 1); CuAssertIntEquals(tc, 1, f->no); - faction_setpassword(f, "password"); + faction_setpassword(f, "patzword"); f->lastorders = turn - 1; - olist.orders[0] = "ERESSEA 1 patzword"; - olist.orders[1] = NULL; + olist.orders = orders; olist.next = 0; in.getbuf = getbuf_list; in.data = &olist;