start to get rid of getstrtoken(), which uses an internal static buffer and causes horrible bugs when called multiple times.

This commit is contained in:
Enno Rehling 2014-12-22 14:21:24 +01:00
parent 0b58ca9176
commit 5cdc85f6bf
12 changed files with 151 additions and 111 deletions

View File

@ -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);

View File

@ -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;
}

View File

@ -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());

View File

@ -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

View File

@ -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 "<Spruchname>" NICHT löscht diesen speziellen

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -2,7 +2,7 @@
* +-------------------+ Christian Schlittchen <corwin@amber.kn-bremen.de>
* | | Enno Rehling <enno@eressea.de>
* | Eressea PBEM host | Katja Zedel <katze@felidae.kn-bremen.de>
* | (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()

15
src/util/parser.test.c Normal file
View File

@ -0,0 +1,15 @@
#include <platform.h>
#include "parser.h"
#include <CuTest.h>
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;
}