# HG changeset patch # User Matti Hamalainen # Date 1578167867 -7200 # Node ID 537420ccd05b1994daa2a69530d645908cb9fa25 # Parent c9b57c8fd0580f15c849ed9803daa6a476f5a827 More work on STIL parser. diff -r c9b57c8fd058 -r 537420ccd05b sidlib.c --- a/sidlib.c Sat Jan 04 19:00:09 2020 +0200 +++ b/sidlib.c Sat Jan 04 21:57:47 2020 +0200 @@ -10,8 +10,9 @@ BOOL sidlib_fread_str(th_ioctx *ctx, char **str, const size_t len) { - char *tmp = th_malloc(len + 1); - if (tmp == NULL) + char *tmp; + + if ((*str = tmp = th_malloc(len + 1)) == NULL) { th_io_error(ctx, THERR_MALLOC, "Could not allocate %" PRIu_SIZE_T " bytes for a string.", @@ -28,7 +29,6 @@ } tmp[len] = 0; - *str = tmp; return TRUE; err: @@ -594,14 +594,11 @@ } -static int sidlib_stildb_node_realloc(SIDLibSTILNode *node, const int nsubTunes) +static int sidlib_stildb_entry_realloc(SIDLibSTILNode *node, const int nsubTunes) { if (node == NULL) return THERR_NULLPTR; - if (nsubTunes <= 0) - return THERR_INVALID_ARGS; - // Re-allocate subTune structure if needed if (nsubTunes > node->nsubTunes) { @@ -676,7 +673,7 @@ if ((node->filename = th_strdup(filename)) == NULL) return THERR_MALLOC; - if ((res = sidlib_stildb_node_realloc(node, 1)) != THERR_OK) + if ((res = sidlib_stildb_entry_realloc(node, 1)) != THERR_OK) { sidlib_stildb_node_free(node); return res; @@ -710,6 +707,8 @@ "AUTHOR", "TITLE", "INFO", + "ARTIST", + "COMMENT", // STF_LAST }; @@ -718,6 +717,7 @@ typedef struct { size_t lineNum; + ssize_t linePos; int ch, prevMode, nextMode, parseMode; BOOL lineStart; } SIDLibSTILParserCtx; @@ -754,10 +754,12 @@ int sidlib_stildb_read(th_ioctx *fh, SIDLibSTILDB *dbh) { - int ret = THERR_OK; + int ret = THERR_OK, field = -1; SIDLibSTILParserCtx ctx; - char *tmpStr = NULL; + char *tmpStr = NULL, *fieldName = NULL; size_t strPos; + SIDLibSTILNode *entry = NULL; + unsigned int subtune; if (fh == NULL || dbh == NULL) return THERR_NULLPTR; @@ -773,9 +775,271 @@ strPos = 0; // Parse the STIL database + while (ctx.parseMode != PM_EOF && ctx.parseMode != PM_ERROR) + { + if (ctx.ch == -1) + { + // Get next character + switch (ctx.ch = thfgetc(fh)) + { + case EOF: + if (ctx.parseMode != PM_IDLE) + { + ret = th_io_error(fh, THERR_OUT_OF_DATA, + "Unexpected end of file on line #%d", + fh->line); + ctx.parseMode = PM_ERROR; + } + else + ctx.parseMode = PM_EOF; + break; + + case '\n': + fh->line++; + ctx.linePos = -1; + break; + + default: + if (ctx.linePos < 0) + { + ctx.lineStart = TRUE; + ctx.linePos = 0; + } + else + ctx.linePos++; + } + } + + switch (ctx.parseMode) + { + case PM_COMMENT: + // Comment parsing mode + if (ctx.ch == '\n') + { + // End of line, end of comment + sidlib_stildb_set_parsemode(&ctx, ctx.prevMode); + } + ctx.ch = -1; + break; + + case PM_NEXT: + case PM_IDLE: + // Normal parsing mode + if (ctx.ch == '#') + { + sidlib_stildb_set_parsemode(&ctx, PM_COMMENT); + ctx.ch = -1; + } + else + if (th_iscrlf(ctx.ch) || th_isspace(ctx.ch)) + { + ctx.ch = -1; + } + else + if (ctx.parseMode == PM_IDLE) + { + // PM_IDLE + strPos = 0; + if (ctx.ch == '/') + { + sidlib_stildb_set_parsemode(&ctx, PM_ENTRY); + } + else + if (ctx.ch == '(') + { + sidlib_stildb_set_parsemode(&ctx, PM_SUBTUNE); + ctx.ch = -1; + ctx.lineStart = TRUE; + } + else + if (th_isalpha(ctx.ch)) + { + sidlib_stildb_set_parsemode(&ctx, PM_FIELD_NAME); + } + else + { + // Error! Invalid character found + ret = th_io_error(fh, THERR_INVALID_DATA, + "Unexpected character '%c' on line #%d.", + ctx.ch, fh->line); + goto out; + } + } + else + { + // PM_NEXT - Next item found + sidlib_stildb_set_parsemode(&ctx, ctx.nextMode); + } + break; + + case PM_ENTRY: + if (th_iscrlf(ctx.ch) || th_isspace(ctx.ch)) + { + // A new file / entry, allocate and append + tmpStr[strPos] = 0; + + if ((ret = sidlib_stildb_node_new(&entry, tmpStr)) != THERR_OK) + goto out; + + th_llist_append_node((th_llist_t **) &dbh->nodes, (th_llist_t *) entry); + subtune = 0; + + sidlib_stildb_set_parsemode(&ctx, PM_IDLE); + } + else + { + VADDCH(ctx.ch) + else + { + ret = th_io_error(fh, THERR_INVALID_DATA, + "Entry filename too long on line #%d", + fh->line); + goto out; + } + ctx.ch = -1; + } + break; + + case PM_SUBTUNE: + if (ctx.ch == ')') + { + BOOL neg = FALSE; + + // Subtune indicator + tmpStr[strPos] = 0; + + if (!th_get_int(tmpStr, &subtune, &neg) || neg) + { + ret = th_io_error(fh, THERR_INVALID_DATA, + "Entry '%s' subtune indicator not a valid integer on line #%d: '%s'", + entry->filename, fh->line, tmpStr); + goto out; + } + + sidlib_stildb_set_parsemode(&ctx, PM_IDLE); + } + else + if (th_isdigit(ctx.ch)) + { + if (ctx.lineStart) + goto sub_unexpected; + + VADDCH(ctx.ch) + else + { + ret = th_io_error(fh, THERR_INVALID_DATA, + "Subtune indicator too long on line #%d.", + fh->line); + goto out; + } + } + else + if ((ctx.ch == '#' && strPos > 0) || (ctx.ch != '#' && !th_isspace(ctx.ch))) + { +sub_unexpected: + ret = th_io_error(fh, THERR_INVALID_DATA, + "Unexpected character '%c' in subtune indicator on line #%d.", + ctx.ch, fh->line); + goto out; + } + + ctx.ch = -1; + ctx.lineStart = FALSE; + break; + + case PM_FIELD_NAME: + if (ctx.ch == ':' || th_iscrlf(ctx.ch) || th_isspace(ctx.ch)) + { + tmpStr[strPos] = 0; + th_pstr_cpy(&fieldName, tmpStr); + + field = sidlib_stildb_get_field(tmpStr); + + if (entry == NULL) + { + ret = th_io_error(fh, THERR_INVALID_DATA, + "No STIL entry allocated, but field '%s' found on line #%d.", + fieldName, fh->line); + goto out; + } + + sidlib_stildb_set_next_parsemode(&ctx, PM_FIELD_DATA); + ctx.lineStart = FALSE; + strPos = 0; + } + else + { + VADDCH(ctx.ch) + else + { + ret = th_io_error(fh, THERR_INVALID_DATA, + "Field name too long on line #%d.", + fieldName, fh->line); + goto out; + } + } + + ctx.ch = -1; + break; + + case PM_FIELD_DATA: + if (ctx.lineStart && th_isspace(ctx.ch) && ctx.linePos < 8) + { + ctx.ch = -1; + } + else + if (th_iscrlf(ctx.ch)) + { + ctx.lineStart = TRUE; + ctx.ch = -1; + } + else + if (ctx.lineStart && ctx.linePos < 8) + { + // Field done + tmpStr[strPos] = 0; + + if (field >= 0) + { + char *data; + + // Supported field, add it + if ((ret = sidlib_stildb_entry_realloc(entry, subtune)) != THERR_OK) + goto out; + + if ((data = th_strdup(tmpStr)) == NULL) + { + ret = th_io_error(fh, THERR_MALLOC, + "Could not allocate memory for field '%s' on line #%d.", + fieldName, fh->line); + goto out; + } + + entry->subTunes[subtune]->fields[field] = data; + } + + sidlib_stildb_set_parsemode(&ctx, PM_IDLE); + } + else + { + ctx.lineStart = FALSE; + VADDCH(ctx.ch) + else + { + ret = th_io_error(fh, THERR_INVALID_DATA, + "Field '%s' data too long on line #%d.", + fieldName, fh->line); + goto out; + } + ctx.ch = - 1; + } + break; + } + } out: th_free(tmpStr); + th_free(fieldName); if (ret == THERR_OK && ctx.parseMode == PM_ERROR) ret = THERR_INVALID_DATA; diff -r c9b57c8fd058 -r 537420ccd05b sidlib.h --- a/sidlib.h Sat Jan 04 19:00:09 2020 +0200 +++ b/sidlib.h Sat Jan 04 21:57:47 2020 +0200 @@ -54,6 +54,8 @@ STF_AUTHOR, STF_TITLE, STF_INFO, + STF_ARTIST, + STF_COMMENT, STF_LAST };