diff --git a/src/economy.c b/src/economy.c index 2553a973d..7c8321d88 100644 --- a/src/economy.c +++ b/src/economy.c @@ -1485,6 +1485,7 @@ static void create_item(unit * u, const item_type * itype, int want) int make_cmd(unit * u, struct order *ord) { + char token[128]; region *r = u->region; const building_type *btype = 0; const ship_type *stype = 0; @@ -1498,15 +1499,16 @@ int make_cmd(unit * u, struct order *ord) kwd = init_order(ord); assert(kwd == K_MAKE); - s = getstrtoken(); + s = getstrtok(token, sizeof(token)); if (s) { m = atoi((const char *)s); sprintf(ibuf, "%d", m); if (!strcmp(ibuf, (const char *)s)) { /* a quantity was given */ - s = getstrtoken(); - } else { + s = getstrtok(token, sizeof(token)); + } + else { m = INT_MAX; } if (s) { @@ -1520,7 +1522,7 @@ int make_cmd(unit * u, struct order *ord) cmistake(u, ord, 275, MSG_PRODUCE); } else { - const char * s = getstrtoken(); + const char * s = getstrtok(token, sizeof(token)); direction_t d = s ? get_direction(s, u->faction->locale) : NODIRECTION; if (d != NODIRECTION) { build_road(r, u, m, d); diff --git a/src/kernel/order.c b/src/kernel/order.c index 93e3d6c96..93692b9d1 100644 --- a/src/kernel/order.c +++ b/src/kernel/order.c @@ -207,7 +207,7 @@ static order_data *create_data(keyword_t kwd, const char *sptr, int lindex) /* learning, only one order_data per skill required */ if (kwd == K_STUDY) { - skill_t sk = get_skill(parse_token(&sptr), lang); + skill_t sk = get_skill(parse_token_depr(&sptr), lang); switch (sk) { case NOSKILL: /* fehler */ break; @@ -364,11 +364,11 @@ order *parse_order(const char *s, const struct locale * lang) ++s; } sptr = s; - p = *sptr ? parse_token(&sptr) : 0; + p = *sptr ? parse_token_depr(&sptr) : 0; kwd = p ? get_keyword(p, lang) : NOKEYWORD; if (kwd == K_MAKE) { const char *s, *sp = sptr; - s = parse_token(&sp); + s = parse_token_depr(&sp); if (s && isparam(s, lang, P_TEMP)) { kwd = K_MAKETEMP; sptr = sp; @@ -570,6 +570,6 @@ keyword_t init_order(const struct order *ord) assert(ord && ord->data); if (ord->data->_str) cmd = _strdup(ord->data->_str); - init_tokens_str(cmd, cmd); + init_tokens_str(cmd); return ord->data->_keyword; } diff --git a/src/kernel/order.test.c b/src/kernel/order.test.c index f0f9a814d..a45752c95 100644 --- a/src/kernel/order.test.c +++ b/src/kernel/order.test.c @@ -128,19 +128,19 @@ static void test_init_order(CuTest *tc) { static void test_getstrtoken(CuTest *tc) { char *cmd = _strdup("hurr \"durr\" \"\" \'\'"); - init_tokens_str(cmd, cmd); + init_tokens_str(cmd); CuAssertStrEquals(tc, "hurr", getstrtoken()); CuAssertStrEquals(tc, "durr", getstrtoken()); CuAssertStrEquals(tc, "", getstrtoken()); CuAssertStrEquals(tc, "", getstrtoken()); CuAssertStrEquals(tc, 0, getstrtoken()); - init_tokens_str(0, 0); + init_tokens_str(0); CuAssertStrEquals(tc, 0, getstrtoken()); } static void test_skip_token(CuTest *tc) { char *cmd = _strdup("hurr \"durr\""); - init_tokens_str(cmd, cmd); + init_tokens_str(cmd); skip_token(); CuAssertStrEquals(tc, "durr", getstrtoken()); CuAssertStrEquals(tc, 0, getstrtoken()); diff --git a/src/kernel/save.c b/src/kernel/save.c index 3671b07fe..f47c3a9be 100644 --- a/src/kernel/save.c +++ b/src/kernel/save.c @@ -169,7 +169,7 @@ static unit *unitorders(FILE * F, int enc, struct faction *f) if (s[0]) { if (s[0] != '@') { const char *stok = s; - stok = parse_token(&stok); + stok = parse_token_depr(&stok); if (stok) { bool quit = false; @@ -267,11 +267,12 @@ int readorders(const char *filename) * 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, NULL); - s = getstrtoken(); + init_tokens_str(b); + s = getstrtok(token, sizeof(token)); p = s ? findparam(s, lang) : NOPARAM; switch (p) { #undef LOCALE_CHANGE @@ -308,7 +309,7 @@ int readorders(const char *filename) if (!b) { break; } - init_tokens_str(b, NULL); + init_tokens_str(b); b = getstrtoken(); p = (!b || b[0] == '@') ? NOPARAM : findparam(b, lang); } while ((p != P_UNIT || !f) && p != P_FACTION && p != P_NEXT diff --git a/src/laws.c b/src/laws.c index 968f36137..341d2b656 100755 --- a/src/laws.c +++ b/src/laws.c @@ -1159,13 +1159,14 @@ void do_enter(struct region *r, bool is_final_attempt) while (*ordp) { order *ord = *ordp; if (getkeyword(ord) == K_ENTER) { + char token[128]; param_t p; int id; unit *ulast = NULL; const char * s; init_order(ord); - s = getstrtoken(); + s = getstrtok(token, sizeof(token)); p = findparam_ex(s, u->faction->locale); id = getid(); @@ -2613,11 +2614,12 @@ int guard_off_cmd(unit * u, struct order *ord) int reshow_cmd(unit * u, struct order *ord) { + char lbuf[64]; const char *s; param_t p = NOPARAM; init_order(ord); - s = getstrtoken(); + s = getstrtok(lbuf, sizeof(lbuf)); if (s && isparam(s, u->faction->locale, P_ANY)) { p = getparam(u->faction->locale); @@ -2676,12 +2678,13 @@ int status_cmd(unit * u, struct order *ord) int combatspell_cmd(unit * u, struct order *ord) { + char token[128]; const char *s; int level = 0; spell *sp = 0; init_order(ord); - s = getstrtoken(); + s = getstrtok(token, sizeof(token)); /* KAMPFZAUBER [NICHT] löscht alle gesetzten Kampfzauber */ if (!s || *s == 0 || findparam(s, u->faction->locale) == P_NOT) { @@ -2694,7 +2697,7 @@ int combatspell_cmd(unit * u, struct order *ord) /* Merken, setzen kommt erst später */ level = getint(); level = _max(0, level); - s = getstrtoken(); + s = getstrtok(token, sizeof(token)); } sp = unit_getspell(u, s, u->faction->locale); @@ -2703,7 +2706,7 @@ int combatspell_cmd(unit * u, struct order *ord) return 0; } - s = getstrtoken(); + s = getstrtok(token, sizeof(token)); if (findparam(s, u->faction->locale) == P_NOT) { /* KAMPFZAUBER "" NICHT löscht diesen speziellen diff --git a/src/magic.c b/src/magic.c index 8fc5072f6..d1bd6c661 100644 --- a/src/magic.c +++ b/src/magic.c @@ -2495,6 +2495,7 @@ static bool is_moving_ship(const region * r, ship * sh) static castorder *cast_cmd(unit * u, order * ord) { + char token[128]; region *r = u->region; region *target_r = r; int level, range; @@ -2518,7 +2519,7 @@ static castorder *cast_cmd(unit * u, order * ord) level = eff_skill(u, SK_MAGIC, r); init_order(ord); - s = getstrtoken(); + s = getstrtok(token, sizeof(token)); param = findparam(s, u->faction->locale); /* für Syntax ' STUFE x REGION y z ' */ if (param == P_LEVEL) { @@ -2529,7 +2530,7 @@ static castorder *cast_cmd(unit * u, order * ord) cmistake(u, ord, 10, MSG_MAGIC); return 0; } - s = getstrtoken(); + s = getstrtok(token, sizeof(token)); param = findparam(s, u->faction->locale); } if (param == P_REGION) { @@ -2546,7 +2547,7 @@ static castorder *cast_cmd(unit * u, order * ord) "unit region command", u, u->region, ord)); return 0; } - s = getstrtoken(); + s = getstrtok(token, sizeof(token)); param = findparam(s, u->faction->locale); } /* für Syntax ' REGION x y STUFE z ' @@ -2559,7 +2560,7 @@ static castorder *cast_cmd(unit * u, order * ord) cmistake(u, ord, 10, MSG_MAGIC); return 0; } - s = getstrtoken(); + s = getstrtok(token, sizeof(token)); } if (!s || !s[0] || strlen(s) == 0) { /* Fehler "Es wurde kein Zauber angegeben" */ @@ -2935,7 +2936,7 @@ spell *unit_getspell(struct unit *u, const char *name, const struct locale * lan if (sb) { select_spellbook(&tokens, sb, lang); } - +#if 0 // TODO: some familiars can cast spells from the mage's spellbook? u = get_familiar_mage(u); if (u) { sb = unit_get_spellbook(u); @@ -2943,6 +2944,7 @@ spell *unit_getspell(struct unit *u, const char *name, const struct locale * lan select_spellbook(&tokens, sb, lang); } } +#endif if (tokens) { variant token; diff --git a/src/move.c b/src/move.c index 69486d2e9..cadb6dca6 100644 --- a/src/move.c +++ b/src/move.c @@ -2615,7 +2615,7 @@ static int hunt(unit * u, order * ord) /* In command steht jetzt das NACH-Kommando. */ /* NACH ignorieren und Parsing initialisieren. */ - init_tokens_str(command, NULL); + init_tokens_str(command); getstrtoken(); /* NACH ausführen */ move(u, false); diff --git a/src/test_eressea.c b/src/test_eressea.c index ac18fc0b2..caa54b067 100644 --- a/src/test_eressea.c +++ b/src/test_eressea.c @@ -48,6 +48,7 @@ int RunAllTests(void) RUN_TESTS(suite, base36); RUN_TESTS(suite, bsdstring); RUN_TESTS(suite, functions); + RUN_TESTS(suite, parser); RUN_TESTS(suite, umlaut); RUN_TESTS(suite, unicode); RUN_TESTS(suite, strings); diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index a9fde29a8..a08046ae2 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -2,6 +2,7 @@ project(util C) SET(_TEST_FILES base36.test.c +parser.test.c attrib.test.c strings.test.c bsdstring.test.c diff --git a/src/util/parser.c b/src/util/parser.c index 2520fd170..12ac19a6e 100644 --- a/src/util/parser.c +++ b/src/util/parser.c @@ -15,7 +15,6 @@ typedef struct parser_state { const char *current_token; - char *current_cmd; struct parser_state *next; } parser_state; @@ -50,22 +49,17 @@ static int eatwhitespace_c(const char **str_p) return ret; } -void init_tokens_str(const char *initstr, char *cmd) +void init_tokens_str(const char *initstr) { if (states == NULL) { states = malloc(sizeof(parser_state)); } - else if (states->current_cmd && states->current_cmd!=cmd) { - free(states->current_cmd); - } - states->current_cmd = cmd; states->current_token = initstr; } void parser_pushstate(void) { parser_state *new_state = malloc(sizeof(parser_state)); - new_state->current_cmd = NULL; new_state->current_token = NULL; new_state->next = states; states = new_state; @@ -74,8 +68,6 @@ void parser_pushstate(void) void parser_popstate(void) { parser_state *new_state = states->next; - if (states->current_cmd != NULL) - free(states->current_cmd); free(states); states = new_state; } @@ -128,83 +120,104 @@ void skip_token(void) } } -const char *parse_token(const char **str) +char *parse_token(const char **str, char *lbuf, size_t len) +{ + char *cursor = lbuf; + char quotechar = 0; + bool escape = false; + const char *ctoken = *str; + + if (!ctoken) { + return 0; + } + eatwhitespace_c(&ctoken); + if (!*ctoken) { + return 0; + } + while (*ctoken && cursor-len < lbuf-1) { + ucs4_t ucs; + size_t len; + bool copy = false; + + unsigned char utf8_character = *(unsigned char *)ctoken; + if (~utf8_character & 0x80) { + ucs = utf8_character; + len = 1; + } + else { + int ret = unicode_utf8_to_ucs4(&ucs, ctoken, &len); + if (ret != 0) { + log_warning("illegal character sequence in UTF8 string: %s\n", ctoken); + break; + } + } + if (escape) { + copy = true; + escape = false; + } + else if (iswxspace((wint_t)ucs)) { + if (quotechar == 0) + break; + copy = true; + } + else if (utf8_character == '"' || utf8_character == '\'') { + if (utf8_character == quotechar) { + ++ctoken; + break; + } + else if (quotechar == 0) { + quotechar = utf8_character; + ++ctoken; + } + else { + *cursor++ = *ctoken++; + } + } + else if (utf8_character == SPACE_REPLACEMENT) { + *cursor++ = ' '; + ++ctoken; + } + else if (utf8_character == ESCAPE_CHAR) { + escape = true; + ++ctoken; + } + else { + copy = true; + } + if (copy) { + memcpy(cursor, ctoken, len); + cursor += len; + ctoken += len; + } + } + + assert(cursor - len < lbuf - 1); // TODO: handle too-small buffers + *cursor = '\0'; + *str = ctoken; + return lbuf; +} + +const char *parse_token_depr(const char **str) { static char lbuf[MAXTOKENSIZE]; /* STATIC_RESULT: used for return, not across calls */ - char *cursor = lbuf; - char quotechar = 0; - bool escape = false; - const char *ctoken = *str; - - if (!ctoken) { - return 0; - } - eatwhitespace_c(&ctoken); - if (!*ctoken) { - return 0; - } - while (*ctoken && cursor - lbuf < MAXTOKENSIZE - 1) { - ucs4_t ucs; - size_t len; - bool copy = false; - - unsigned char utf8_character = *(unsigned char *)ctoken; - if (~utf8_character & 0x80) { - ucs = utf8_character; - len = 1; - } else { - int ret = unicode_utf8_to_ucs4(&ucs, ctoken, &len); - if (ret != 0) { - log_warning("illegal character sequence in UTF8 string: %s\n", ctoken); - break; - } - } - if (escape) { - copy = true; - escape = false; - } else if (iswxspace((wint_t) ucs)) { - if (quotechar == 0) - break; - copy = true; - } else if (utf8_character == '"' || utf8_character == '\'') { - if (utf8_character == quotechar) { - ++ctoken; - break; - } else if (quotechar == 0) { - quotechar = utf8_character; - ++ctoken; - } else { - *cursor++ = *ctoken++; - } - } else if (utf8_character == SPACE_REPLACEMENT) { - *cursor++ = ' '; - ++ctoken; - } else if (utf8_character == ESCAPE_CHAR) { - escape = true; - ++ctoken; - } else { - copy = true; - } - if (copy) { - memcpy(cursor, ctoken, len); - cursor += len; - ctoken += len; - } - } - - *cursor = '\0'; - *str = ctoken; - return lbuf; + return parse_token(str, lbuf, MAXTOKENSIZE); } const char *getstrtoken(void) { - return parse_token((const char **)&states->current_token); + char lbuf[MAXTOKENSIZE]; + return parse_token((const char **)&states->current_token, lbuf, MAXTOKENSIZE); +} + +const char *getstrtok(char *lbuf, size_t bufsize) +{ + return parse_token((const char **)&states->current_token, lbuf, bufsize); } int getid(void) { - const char *str = getstrtoken(); + char token[16]; + const char *str = getstrtok(token, sizeof(token)); int i = str ? atoi36(str) : 0; if (i < 0) { return -1; diff --git a/src/util/parser.h b/src/util/parser.h index c268ad22a..2ab7f1b75 100644 --- a/src/util/parser.h +++ b/src/util/parser.h @@ -2,7 +2,7 @@ * +-------------------+ Christian Schlittchen * | | Enno Rehling * | Eressea PBEM host | Katja Zedel - * | (c) 1998 - 2007 | + * | (c) 1998 - 2007 | * | | This program may not be used, modified or distributed * +-------------------+ without prior permission by the authors of Eressea. * @@ -14,14 +14,16 @@ extern "C" { #endif - void init_tokens_str(const char *initstr, char *cmd); /* initialize token parsing, take ownership of cmd */ - void skip_token(void); - const char *parse_token(const char **str); - void parser_pushstate(void); - void parser_popstate(void); - bool parser_end(void); - const char *getstrtoken(void); - int getid(void); + void init_tokens_str(const char *initstr); /* initialize token parsing */ + void skip_token(void); + const char *parse_token_depr(const char **str); + char *parse_token(const char **str, char *buffer, size_t bufsize); + void parser_pushstate(void); + void parser_popstate(void); + bool parser_end(void); + const char *getstrtoken(void); + const char *getstrtok(char *lbuf, size_t bufsize); + int getid(void); #define getshipid() getid() #define getfactionid() getid() diff --git a/src/util/parser.test.c b/src/util/parser.test.c new file mode 100644 index 000000000..f5543a7cb --- /dev/null +++ b/src/util/parser.test.c @@ -0,0 +1,15 @@ +#include +#include "parser.h" + +#include + +static void test_parser(CuTest *tc) { + init_tokens_str("HELP ONE TWO THREE"); +} + +CuSuite *get_parser_suite(void) +{ + CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_parser); + return suite; +}