From 050d32ae204b64f7098b12c77cdb92a33ebc7562 Mon Sep 17 00:00:00 2001
From: "Rafael G. Martins" <rafael@rafaelmartins.eng.br>
Date: Fri, 29 May 2020 01:23:04 +0200
Subject: blogc: added filelist parser

---
 .gitignore                          |   1 +
 Makefile.am                         |  21 ++++++
 src/blogc/filelist-parser.c         |  69 +++++++++++++++++++
 src/blogc/filelist-parser.h         |  16 +++++
 tests/blogc/check_filelist_parser.c | 131 ++++++++++++++++++++++++++++++++++++
 5 files changed, 238 insertions(+)
 create mode 100644 src/blogc/filelist-parser.c
 create mode 100644 src/blogc/filelist-parser.h
 create mode 100644 tests/blogc/check_filelist_parser.c

diff --git a/.gitignore b/.gitignore
index e97005c..5336760 100644
--- a/.gitignore
+++ b/.gitignore
@@ -57,6 +57,7 @@ blogc*.html
 /tests/blogc/check_blogc.sh
 /tests/blogc/check_content_parser
 /tests/blogc/check_datetime_parser
+/tests/blogc/check_filelist_parser
 /tests/blogc/check_funcvars
 /tests/blogc/check_loader
 /tests/blogc/check_renderer
diff --git a/Makefile.am b/Makefile.am
index 040f291..3f00119 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -40,6 +40,7 @@ noinst_HEADERS = \
 	src/blogc/content-parser.h \
 	src/blogc/datetime-parser.h \
 	src/blogc/debug.h \
+	src/blogc/filelist-parser.h \
 	src/blogc/funcvars.h \
 	src/blogc/loader.h \
 	src/blogc/renderer.h \
@@ -128,6 +129,7 @@ libblogc_la_SOURCES = \
 	src/blogc/content-parser.c \
 	src/blogc/datetime-parser.c \
 	src/blogc/debug.c \
+	src/blogc/filelist-parser.c \
 	src/blogc/funcvars.c \
 	src/blogc/loader.c \
 	src/blogc/renderer.c \
@@ -468,6 +470,7 @@ if USE_CMOCKA
 check_PROGRAMS += \
 	tests/blogc/check_content_parser \
 	tests/blogc/check_datetime_parser \
+	tests/blogc/check_filelist_parser \
 	tests/blogc/check_renderer \
 	tests/blogc/check_source_parser \
 	tests/blogc/check_template_parser \
@@ -660,6 +663,24 @@ tests_blogc_check_datetime_parser_LDADD = \
 	libblogc_common.la \
 	$(NULL)
 
+tests_blogc_check_filelist_parser_SOURCES = \
+	tests/blogc/check_filelist_parser.c \
+	$(NULL)
+
+tests_blogc_check_filelist_parser_CFLAGS = \
+	$(CMOCKA_CFLAGS) \
+	$(NULL)
+
+tests_blogc_check_filelist_parser_LDFLAGS = \
+	-no-install \
+	$(NULL)
+
+tests_blogc_check_filelist_parser_LDADD = \
+	$(CMOCKA_LIBS) \
+	libblogc.la \
+	libblogc_common.la \
+	$(NULL)
+
 tests_blogc_check_renderer_SOURCES = \
 	tests/blogc/check_renderer.c \
 	$(NULL)
diff --git a/src/blogc/filelist-parser.c b/src/blogc/filelist-parser.c
new file mode 100644
index 0000000..ac9c50a
--- /dev/null
+++ b/src/blogc/filelist-parser.c
@@ -0,0 +1,69 @@
+/*
+ * blogc: A blog compiler.
+ * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ *
+ * This program can be distributed under the terms of the BSD License.
+ * See the file LICENSE.
+ */
+
+#include "../common/utils.h"
+
+
+typedef enum {
+    LINE_START = 1,
+    LINE,
+} blogc_filelist_parser_state_t;
+
+
+bc_slist_t*
+blogc_filelist_parse(const char *src, size_t src_len)
+{
+    size_t current = 0;
+    size_t start = 0;
+    bc_slist_t *rv = NULL;
+    bc_string_t *line = bc_string_new();
+    blogc_filelist_parser_state_t state = LINE_START;
+
+    while (current < src_len) {
+        char c = src[current];
+        bool is_last = current == src_len - 1;
+
+        switch (state) {
+
+            case LINE_START:
+                if (c == '#') {
+                    while (current < src_len) {
+                        if (src[current] == '\r' || src[current] == '\n')
+                            break;
+                        current++;
+                    }
+                    break;
+                }
+                if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
+                    break;
+                start = current;
+                state = LINE;
+                continue;
+
+            case LINE:
+                if (c == '\r' || c == '\n' || is_last) {
+                    if (is_last && c != '\r' && c != '\n')
+                        bc_string_append_c(line, c);
+                    rv = bc_slist_append(rv, bc_str_strip(line->str));
+                    bc_string_free(line, false);
+                    line = bc_string_new();
+                    state = LINE_START;
+                    break;
+                }
+                bc_string_append_c(line, c);
+                break;
+
+        }
+
+        current++;
+    }
+
+    bc_string_free(line, true);
+
+    return rv;
+}
diff --git a/src/blogc/filelist-parser.h b/src/blogc/filelist-parser.h
new file mode 100644
index 0000000..5c65ee0
--- /dev/null
+++ b/src/blogc/filelist-parser.h
@@ -0,0 +1,16 @@
+/*
+ * blogc: A blog compiler.
+ * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ *
+ * This program can be distributed under the terms of the BSD License.
+ * See the file LICENSE.
+ */
+
+#ifndef _FILELIST_PARSER_H
+#define _FILELIST_PARSER_H
+
+#include "../common/utils.h"
+
+bc_slist_t* blogc_filelist_parse(const char *src, size_t src_len);
+
+#endif /* _FILELIST_PARSER_H */
diff --git a/tests/blogc/check_filelist_parser.c b/tests/blogc/check_filelist_parser.c
new file mode 100644
index 0000000..0db50ec
--- /dev/null
+++ b/tests/blogc/check_filelist_parser.c
@@ -0,0 +1,131 @@
+/*
+ * blogc: A blog compiler.
+ * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ *
+ * This program can be distributed under the terms of the BSD License.
+ * See the file LICENSE.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <string.h>
+#include <stdlib.h>
+#include "../../src/common/utils.h"
+#include "../../src/blogc/filelist-parser.h"
+
+
+static void
+test_filelist_parse_empty(void **state)
+{
+    bc_slist_t *l = blogc_filelist_parse("", 0);
+    assert_null(l);
+}
+
+
+static void
+test_filelist_parse(void **state)
+{
+    const char *a =
+        "content/post/post1.txt";
+    bc_slist_t *l = blogc_filelist_parse(a, strlen(a));
+    assert_non_null(l);
+    assert_string_equal(l->data, "content/post/post1.txt");
+    assert_null(l->next);
+    bc_slist_free_full(l, free);
+    a =
+        "content/post/post1.txt\n";
+    l = blogc_filelist_parse(a, strlen(a));
+    assert_non_null(l);
+    assert_string_equal(l->data, "content/post/post1.txt");
+    assert_null(l->next);
+    bc_slist_free_full(l, free);
+    a =
+        "content/post/post1.txt\n"
+        "content/post/post2.txt\n";
+    l = blogc_filelist_parse(a, strlen(a));
+    assert_non_null(l);
+    assert_string_equal(l->data, "content/post/post1.txt");
+    assert_string_equal(l->next->data, "content/post/post2.txt");
+    assert_null(l->next->next);
+    bc_slist_free_full(l, free);
+    a =
+        "content/post/post1.txt\n"
+        "content/post/post2.txt\n"
+        "content/post/post3.txt";
+    l = blogc_filelist_parse(a, strlen(a));
+    assert_non_null(l);
+    assert_string_equal(l->data, "content/post/post1.txt");
+    assert_string_equal(l->next->data, "content/post/post2.txt");
+    assert_string_equal(l->next->next->data, "content/post/post3.txt");
+    assert_null(l->next->next->next);
+    bc_slist_free_full(l, free);
+    a =
+        "   content/post/post1.txt\n"
+        "content/post/post2.txt\t\n"
+        "\tcontent/post/post3.txt";
+    l = blogc_filelist_parse(a, strlen(a));
+    assert_non_null(l);
+    assert_string_equal(l->data, "content/post/post1.txt");
+    assert_string_equal(l->next->data, "content/post/post2.txt");
+    assert_string_equal(l->next->next->data, "content/post/post3.txt");
+    assert_null(l->next->next->next);
+    bc_slist_free_full(l, free);
+}
+
+
+static void
+test_filelist_parse_crlf(void **state)
+{
+    const char *a =
+        "content/post/post1.txt\r\n";
+    bc_slist_t *l = blogc_filelist_parse(a, strlen(a));
+    assert_non_null(l);
+    assert_string_equal(l->data, "content/post/post1.txt");
+    assert_null(l->next);
+    bc_slist_free_full(l, free);
+    a =
+        "content/post/post1.txt\r\n"
+        "content/post/post2.txt\r\n";
+    l = blogc_filelist_parse(a, strlen(a));
+    assert_non_null(l);
+    assert_string_equal(l->data, "content/post/post1.txt");
+    assert_string_equal(l->next->data, "content/post/post2.txt");
+    assert_null(l->next->next);
+    bc_slist_free_full(l, free);
+    a =
+        "content/post/post1.txt\r\n"
+        "content/post/post2.txt\r\n"
+        "content/post/post3.txt";
+    l = blogc_filelist_parse(a, strlen(a));
+    assert_non_null(l);
+    assert_string_equal(l->data, "content/post/post1.txt");
+    assert_string_equal(l->next->data, "content/post/post2.txt");
+    assert_string_equal(l->next->next->data, "content/post/post3.txt");
+    assert_null(l->next->next->next);
+    bc_slist_free_full(l, free);
+    a =
+        "   content/post/post1.txt\r\n"
+        "content/post/post2.txt\t\r\n"
+        "\tcontent/post/post3.txt";
+    l = blogc_filelist_parse(a, strlen(a));
+    assert_non_null(l);
+    assert_string_equal(l->data, "content/post/post1.txt");
+    assert_string_equal(l->next->data, "content/post/post2.txt");
+    assert_string_equal(l->next->next->data, "content/post/post3.txt");
+    assert_null(l->next->next->next);
+    bc_slist_free_full(l, free);
+}
+
+
+int
+main(void)
+{
+    const UnitTest tests[] = {
+        unit_test(test_filelist_parse_empty),
+        unit_test(test_filelist_parse),
+        unit_test(test_filelist_parse_crlf),
+    };
+    return run_tests(tests);
+}
-- 
cgit v1.2.3-18-g5258