changeset 242:537420ccd05b

More work on STIL parser.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 04 Jan 2020 21:57:47 +0200
parents c9b57c8fd058
children a3dd87b7c00b
files sidlib.c sidlib.h
diffstat 2 files changed, 276 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- 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;
--- 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
 };