diff --git a/src/bind_eressea.c b/src/bind_eressea.c index 34640137b..e5acbac99 100755 --- a/src/bind_eressea.c +++ b/src/bind_eressea.c @@ -41,7 +41,7 @@ int eressea_read_orders(const char * filename) { return -1; } log_info("reading orders from %s", filename); - return readorders(F); + return parseorders(F); } int eressea_export_json(const char * filename, int flags) { diff --git a/src/checker.c b/src/checker.c index 302c80390..f87b91891 100644 --- a/src/checker.c +++ b/src/checker.c @@ -2,10 +2,50 @@ #include #endif -#include "util/parser.h" +#include "util/order_parser.h" #include +typedef struct parser_state { + FILE * F; +} parser_state; + +static void handle_order(void *userData, const char *str) { + parser_state * state = (parser_state*)userData; + fputs(str, state->F); + fputc('\n', state->F); +} + +int parsefile(FILE *F) { + OP_Parser parser; + char buf[1024]; + int done = 0, err = 0; + parser_state state = { NULL }; + + state.F = stdout; + + parser = OP_ParserCreate(); + OP_SetOrderHandler(parser, handle_order); + OP_SetUserData(parser, &state); + + while (!done) { + size_t len = (int)fread(buf, 1, sizeof(buf), F); + if (ferror(F)) { + /* TODO: error message */ + err = errno; + break; + } + done = feof(F); + if (OP_Parse(parser, buf, len, done) == OP_STATUS_ERROR) { + /* TODO: error message */ + err = (int)OP_GetErrorCode(parser); + break; + } + } + OP_ParserFree(parser); + return err; +} + int main(int argc, char **argv) { FILE * F = stdin; if (argc >= 1) { @@ -15,8 +55,10 @@ int main(int argc, char **argv) { perror(filename); return -1; } + } + parsefile(F); + if (F != stdin) { fclose(F); } return 0; } - diff --git a/src/orderfile.c b/src/orderfile.c index 316724338..90b44477b 100644 --- a/src/orderfile.c +++ b/src/orderfile.c @@ -245,10 +245,15 @@ static void handle_faction(void *userData, int no, const char *password) { log_debug("orders for unknown faction %s", itoa36(no)); } else { - if (!checkpasswd(f, password)) { + if (checkpasswd(f, password)) { + f->lastorders = turn; + } + else { log_debug("invalid password for faction %s", itoa36(no)); ADDMSG(&f->msgs, msg_message("wrongpasswd", "password", password)); } + state->u = NULL; + state->next_order = NULL; } } @@ -272,23 +277,58 @@ static void handle_unit(void *userData, int no) { static void handle_order(void *userData, const char *str) { parser_state *state = (parser_state *)userData; - unit * u = state->u; - order *ord; + const char * tok, *input = str; + char buffer[16]; + const struct locale *lang; + param_t p; + faction * f = state->f; - ord = parse_order(str, u->faction->locale); - if (ord) { - *state->next_order = ord; - state->next_order = &ord->next; + lang = f ? f->locale : default_locale; + tok = parse_token(&input, buffer, sizeof(buffer)); + p = findparam(tok, lang); + if (p == P_FACTION || p == P_GAMENAME) { + tok = parse_token(&input, buffer, sizeof(buffer)); + if (tok) { + int no = atoi36(tok); + tok = parse_token(&input, buffer, sizeof(buffer)); + handle_faction(userData, no, tok); + } + else { + /* TODO: log_error() */ + } } - else { - ADDMSG(&u->faction->msgs, msg_message("parse_error", "unit command", u, str)); + else if (p == P_UNIT) { + tok = parse_token(&input, buffer, sizeof(buffer)); + if (tok) { + int no = atoi36(tok); + handle_unit(userData, no); + } + } + else if (p == P_NEXT) { + state->f = NULL; + state->u = NULL; + state->next_order = NULL; + } + else if (p == P_REGION) { + state->u = NULL; + state->next_order = NULL; + } + else if (state->u) { + unit * u = state->u; + order * ord = parse_order(str, lang); + if (ord) { + *state->next_order = ord; + state->next_order = &ord->next; + } + else { + ADDMSG(&u->faction->msgs, msg_message("parse_error", "unit command", u, str)); + } } } - int parseorders(FILE *F) { - char buf[2048]; + char buf[4096]; int done = 0, err = 0; OP_Parser parser; parser_state state = { NULL, NULL }; @@ -298,8 +338,6 @@ int parseorders(FILE *F) /* TODO: error message */ return errno; } - OP_SetUnitHandler(parser, handle_unit); - OP_SetFactionHandler(parser, handle_faction); OP_SetOrderHandler(parser, handle_order); OP_SetUserData(parser, &state); @@ -313,7 +351,7 @@ int parseorders(FILE *F) done = feof(F); if (OP_Parse(parser, buf, len, done) == OP_STATUS_ERROR) { /* TODO: error message */ - err = -1; + err = (int)OP_GetErrorCode(parser); break; } } @@ -323,16 +361,11 @@ int parseorders(FILE *F) int readorders(FILE *F) { -#undef NEW_PARSER -#ifdef NEW_PARSER - return parseorders(F); -#else - input in; int result; + input in; in.getbuf = file_getbuf; in.data = F; result = read_orders(&in); return result; -#endif } diff --git a/src/orderfile.h b/src/orderfile.h index fa051993e..f4260aa67 100644 --- a/src/orderfile.h +++ b/src/orderfile.h @@ -14,6 +14,7 @@ extern "C" { int read_orders(struct input *in); int readorders(FILE *F); + int parseorders(FILE *F); #ifdef __cplusplus } diff --git a/src/util/order_parser.c b/src/util/order_parser.c index 8bf4c7e8d..21268f163 100644 --- a/src/util/order_parser.c +++ b/src/util/order_parser.c @@ -5,7 +5,7 @@ #include "order_parser.h" #include -#include +#include #include #include @@ -13,21 +13,15 @@ struct OrderParserStruct { void *m_userData; char *m_buffer; char *m_bufferPtr; + size_t m_bufferSize; const char *m_bufferEnd; - OP_FactionHandler m_factionHandler; - OP_UnitHandler m_unitHandler; OP_OrderHandler m_orderHandler; enum OP_Error m_errorCode; int m_lineNumber; }; -void OP_SetUnitHandler(OP_Parser parser, OP_UnitHandler handler) -{ - parser->m_unitHandler = handler; -} - -void OP_SetFactionHandler(OP_Parser parser, OP_FactionHandler handler) { - parser->m_factionHandler = handler; +enum OP_Error OP_GetErrorCode(OP_Parser parser) { + return parser->m_errorCode; } void OP_SetOrderHandler(OP_Parser parser, OP_OrderHandler handler) { @@ -42,6 +36,7 @@ static void buffer_free(OP_Parser parser) { /* TODO: recycle buffers, reduce mallocs. */ free(parser->m_buffer); + parser->m_bufferSize = 0; parser->m_bufferEnd = parser->m_bufferPtr = parser->m_buffer = NULL; } @@ -65,8 +60,28 @@ void OP_ParserFree(OP_Parser parser) { static enum OP_Error buffer_append(OP_Parser parser, const char *s, int len) { + size_t total = len + 1; + size_t remain = parser->m_bufferEnd - parser->m_bufferPtr; + total += remain; + if (remain > 0) { + /* there is remaining data in the buffer, should we move it to the front? */ + if (total <= parser->m_bufferSize) { + /* reuse existing buffer */ + memmove(parser->m_buffer, parser->m_bufferPtr, remain); + memcpy(parser->m_buffer + remain, s, len); + parser->m_buffer[total - 1] = '\0'; + parser->m_bufferPtr = parser->m_buffer; + parser->m_bufferEnd = parser->m_bufferPtr + total - 1; + return OP_ERROR_NONE; + } + } + else if (parser->m_bufferPtr >= parser->m_bufferEnd) { + buffer_free(parser); + } + if (parser->m_buffer == NULL) { - parser->m_buffer = malloc(len + 1); + parser->m_bufferSize = len + 1; + parser->m_buffer = malloc(parser->m_bufferSize); if (!parser->m_buffer) { return OP_ERROR_NO_MEMORY; } @@ -76,28 +91,26 @@ static enum OP_Error buffer_append(OP_Parser parser, const char *s, int len) parser->m_bufferEnd = parser->m_buffer + len; } else { - size_t total = len; char * buffer; - total += (parser->m_bufferEnd - parser->m_bufferPtr); /* TODO: recycle buffers, reduce mallocs. */ - buffer = malloc(total + 1); - memcpy(buffer, parser->m_bufferPtr, total - len); - memcpy(buffer + total - len, s, len); - buffer[total] = '\0'; - free(parser->m_buffer); - parser->m_buffer = buffer; - if (!parser->m_buffer) { - return OP_ERROR_NO_MEMORY; + if (parser->m_bufferSize < total) { + parser->m_bufferSize = total; + buffer = malloc(parser->m_bufferSize); + if (!buffer) { + return OP_ERROR_NO_MEMORY; + } + memcpy(buffer, parser->m_bufferPtr, total - len - 1); + memcpy(buffer + total - len - 1, s, len); + free(parser->m_buffer); + parser->m_buffer = buffer; } + else { + memcpy(parser->m_buffer, parser->m_bufferPtr, total - len); + memcpy(parser->m_buffer + total - len, s, len); + } + parser->m_buffer[total - 1] = '\0'; parser->m_bufferPtr = parser->m_buffer; - parser->m_bufferEnd = parser->m_buffer + total; - } - return OP_ERROR_NONE; -} - -static enum OP_Error handle_line(OP_Parser parser) { - if (parser->m_orderHandler) { - parser->m_orderHandler(parser->m_userData, parser->m_bufferPtr); + parser->m_bufferEnd = parser->m_buffer + total - 1; } return OP_ERROR_NONE; } @@ -105,12 +118,23 @@ static enum OP_Error handle_line(OP_Parser parser) { static char *skip_spaces(char *pos) { char *next; for (next = pos; *next && *next != '\n'; ++next) { + wint_t wch = *(unsigned char *)next; /* TODO: handle unicode whitespace */ - if (!isspace(*next)) break; + if (!iswspace(wch)) break; } return next; } +static enum OP_Error handle_line(OP_Parser parser) { + if (parser->m_orderHandler) { + char * str = skip_spaces(parser->m_bufferPtr); + if (*str) { + parser->m_orderHandler(parser->m_userData, str); + } + } + return OP_ERROR_NONE; +} + static enum OP_Status parse_buffer(OP_Parser parser, int isFinal) { char * pos = strpbrk(parser->m_bufferPtr, "\\;\n"); @@ -118,6 +142,7 @@ static enum OP_Status parse_buffer(OP_Parser parser, int isFinal) enum OP_Error code; size_t len = pos - parser->m_bufferPtr; char *next; + int continue_comment = 0; switch (*pos) { case '\n': @@ -172,18 +197,21 @@ static enum OP_Status parse_buffer(OP_Parser parser, int isFinal) if (next) { if (*next == '\n') { /* no more lines in this comment, we're done: */ - pos = next + 1; ++parser->m_lineNumber; - break; + break; /* exit loop */ } else { /* is this backslash the final character? */ - next = skip_spaces(pos + 1); + next = skip_spaces(next + 1); if (*next == '\n') { /* we have a multi-line comment! */ pos = next + 1; ++parser->m_lineNumber; } + else if (*next == '\0') { + /* cannot find the EOL char yet, stream is dry. keep ; and \ */ + continue_comment = 2; + } else { /* keep looking for a backslash */ pos = next; @@ -192,25 +220,46 @@ static enum OP_Status parse_buffer(OP_Parser parser, int isFinal) } } while (next && *next); - if (next && pos < parser->m_bufferEnd) { - /* we skip the comment, and there is more data in the buffer */ - parser->m_bufferPtr = pos; - } - else { - /* we exhausted the buffer before we got to the end of the comment */ + if (!next) { + /* we exhausted the buffer before we finished the line */ if (isFinal) { - /* the input ended on this comment line, which is fine */ + /* this comment was at the end of the file, it just has no newline. done! */ return OP_STATUS_OK; } else { - /* skip what we have of the comment, keep the semicolon, keep going */ - ptrdiff_t skip = parser->m_bufferEnd - parser->m_bufferPtr; - if (skip > 1) { - parser->m_bufferPtr += (skip - 1); - parser->m_bufferPtr[0] = ';'; - } + /* there is more of this line in the next buffer, save the semicolon */ + continue_comment = 1; } } + else { + if (*next) { + /* end comment parsing, begin parsing a new line */ + pos = next + 1; + continue_comment = 0; + } + else if (!continue_comment) { + /* reached end of input naturally, need more data to finish */ + continue_comment = 1; + } + } + + if (continue_comment) { + ptrdiff_t skip = parser->m_bufferEnd - parser->m_bufferPtr; + assert(skip >= continue_comment); + if (skip >= continue_comment) { + /* should always be true */ + parser->m_bufferPtr += (skip - continue_comment); + parser->m_bufferPtr[0] = ';'; + } + if (continue_comment == 2) { + parser->m_bufferPtr[1] = '\\'; + } + continue_comment = 0; + return OP_STATUS_OK; + } + /* continue the outer loop */ + parser->m_bufferPtr = pos; + pos = strpbrk(pos, "\\;\n"); break; default: parser->m_errorCode = OP_ERROR_SYNTAX; @@ -228,10 +277,6 @@ enum OP_Status OP_Parse(OP_Parser parser, const char *s, int len, int isFinal) { enum OP_Error code; - if (parser->m_bufferPtr >= parser->m_bufferEnd) { - buffer_free(parser); - } - code = buffer_append(parser, s, len); if (code != OP_ERROR_NONE) { parser->m_errorCode = code; diff --git a/src/util/order_parser.h b/src/util/order_parser.h index 10a258875..159fd6594 100644 --- a/src/util/order_parser.h +++ b/src/util/order_parser.h @@ -29,17 +29,14 @@ enum OP_Error { OP_ERROR_SYNTAX }; -typedef void(*OP_FactionHandler) (void *userData, int no, const char *password); -typedef void(*OP_UnitHandler) (void *userData, int no); typedef void(*OP_OrderHandler) (void *userData, const char *str); OP_Parser OP_ParserCreate(void); void OP_ParserFree(OP_Parser parser); void OP_ParserReset(OP_Parser parser); enum OP_Status OP_Parse(OP_Parser parser, const char *s, int len, int isFinal); -void OP_SetUnitHandler(OP_Parser parser, OP_UnitHandler handler); -void OP_SetFactionHandler(OP_Parser parser, OP_FactionHandler handler); void OP_SetOrderHandler(OP_Parser parser, OP_OrderHandler handler); void OP_SetUserData(OP_Parser parser, void *userData); +enum OP_Error OP_GetErrorCode(OP_Parser parser); #endif diff --git a/src/util/order_parser.test.c b/src/util/order_parser.test.c index e9a37fa76..d43f661fb 100644 --- a/src/util/order_parser.test.c +++ b/src/util/order_parser.test.c @@ -32,35 +32,90 @@ static void test_parse_orders(CuTest *tc) { OP_SetUserData(parser, lastline); OP_SetOrderHandler(parser, copy_line); CuAssertPtrNotNull(tc, parser); + lastline[0] = 0; CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Hello World", 11, 1)); CuAssertStrEquals(tc, "Hello World", lastline); OP_ParserReset(parser); + + lastline[0] = 0; + CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Error;\nHello World", 18, 1)); + CuAssertStrEquals(tc, "Hello World", lastline); + OP_ParserReset(parser); + + lastline[0] = 0; + CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Hello World;\\\nError", 19, 1)); + CuAssertStrEquals(tc, "Hello World", lastline); + OP_ParserReset(parser); + + lastline[0] = 0; + CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Hello World;\\", 13, 0)); + CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "\nError", 6, 1)); + CuAssertStrEquals(tc, "Hello World", lastline); + OP_ParserReset(parser); + + lastline[0] = 0; + CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Hello \\", 7, 0)); + CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "\nWorld", 6, 1)); + CuAssertStrEquals(tc, "Hello World", lastline); + OP_ParserReset(parser); + lastline[0] = 0; CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Hello World\n", 12, 1)); CuAssertStrEquals(tc, "Hello World", lastline); OP_ParserReset(parser); + lastline[0] = 0; CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Hello\\\n World", 13, 1)); CuAssertStrEquals(tc, "Hello World", lastline); OP_ParserReset(parser); + lastline[0] = 0; CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Hello;World", 11, 1)); CuAssertStrEquals(tc, "Hello", lastline); OP_ParserReset(parser); + lastline[0] = 0; CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Hello\\World", 11, 1)); CuAssertStrEquals(tc, "Hello\\World", lastline); OP_ParserReset(parser); + lastline[0] = 0; - CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Hello ", 6, 0)); - CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "World", 5, 1)); + CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, ";\n", 2, 0)); + CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Hello World", 11, 1)); CuAssertStrEquals(tc, "Hello World", lastline); OP_ParserReset(parser); + + lastline[0] = 0; + CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, ";Hello \\", 8, 0)); + CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "\nWorld\n", 7, 0)); + CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Enno", 4, 1)); + CuAssertStrEquals(tc, "Enno", lastline); + OP_ParserReset(parser); + + lastline[0] = 0; + CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, ";Hello", 6, 0)); + CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "World\n", 6, 0)); + CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Enno", 4, 1)); + CuAssertStrEquals(tc, "Enno", lastline); + OP_ParserReset(parser); + lastline[0] = 0; CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Hello\\World \\", 14, 1)); CuAssertStrEquals(tc, "Hello\\World ", lastline); OP_ParserReset(parser); + + lastline[0] = 0; + CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Hello\n", 6, 0)); + CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "World\n", 6, 1)); + CuAssertStrEquals(tc, "World", lastline); + OP_ParserReset(parser); + + lastline[0] = 0; + CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Hello\n", 6, 0)); + CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "World\n", 6, 1)); + CuAssertStrEquals(tc, "World", lastline); + OP_ParserReset(parser); lastline[0] = 0; CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Hello \\", 7, 0)); CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "\nWorld", 6, 1));