From 582cbc6cf67919ec189098817ab1e4541c8e761d Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Tue, 28 Apr 2015 02:31:40 -0300 Subject: implemented datetime parsing/formatting --- README.md | 6 ++- configure.ac | 4 +- src/main.c | 8 ++-- src/renderer.c | 91 +++++++++++++++++++++++++++++-------- src/renderer.h | 2 + tests/check_renderer.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 208 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index ad8f40d..89b8b35 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,8 @@ If more than one source file is provided to the compiler with the ``-t`` argumen Variables are single-line, and all the whitespace characters, including tabs, before the leading non-whitespace character and after the trailing non-whitespace character will be removed. +``DATE`` variable, if provided in the correct format, as shown in the example, will be parsed and set to ``DATE_FORMATTED`` variable. User can set ``DATE_FORMAT`` variable with ``strftime(3)`` format string, to change the default formatting, e.g. using the -D command line argument: ``-D DATE_FORMAT="%H:%M:%S"`` + Raw content is available in template blocks as the ``CONTENT`` variable. @@ -69,11 +71,11 @@ Raw content is available in template blocks as the ``CONTENT`` variable.

My cool blog

{% block entry %}

{{ TITLE }}

- {% if DATE %}

Published in: {{ DATE }}

{% endif %} + {% if DATE_FORMATTED %}

Published in: {{ DATE_FORMATTED }}

{% endif %}
{{ CONTENT }}
{% endblock %} {% block listing_once %}{% endblock %} diff --git a/configure.ac b/configure.ac index 040eb44..716daa7 100644 --- a/configure.ac +++ b/configure.ac @@ -9,6 +9,8 @@ AC_CONFIG_HEADERS([config.h]) AM_SILENT_RULES([yes]) AM_MAINTAINER_MODE([enable]) +AC_USE_SYSTEM_EXTENSIONS + LT_INIT AC_PROG_CC_C99 @@ -54,7 +56,7 @@ AS_IF([test "x$have_cmocka" = "xyes"], , [ ]) AM_CONDITIONAL([USE_CMOCKA], [test "x$have_cmocka" = "xyes"]) -AC_CHECK_HEADERS([sys/types.h sys/stat.h]) +AC_CHECK_HEADERS([sys/types.h sys/stat.h time.h]) AC_CONFIG_FILES([ Makefile diff --git a/src/main.c b/src/main.c index 0680728..c112c4f 100644 --- a/src/main.c +++ b/src/main.c @@ -147,10 +147,12 @@ main(int argc, char **argv) goto cleanup; } for (unsigned int j = 0; pieces[0][j] != '\0'; j++) { - if (!(pieces[0][j] >= 'A' && pieces[0][j] <= 'Z')) { + if (!((pieces[0][j] >= 'A' && pieces[0][j] <= 'Z') || + pieces[0][j] == '_')) + { fprintf(stderr, "blogc: error: invalid value " - "for -D (configuration key must be uppercase): " - "%s\n", pieces[0]); + "for -D (configuration key must be uppercase " + "with '_'): %s\n", pieces[0]); b_strv_free(pieces); rv = 2; goto cleanup; diff --git a/src/renderer.c b/src/renderer.c index 905c42f..ac269b8 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -10,13 +10,65 @@ #include #endif /* HAVE_CONFIG_H */ +#ifdef HAVE_TIME_H +#include +#endif /* HAVE_TIME_H */ + +#include #include #include "utils/utils.h" +#include "loader.h" #include "source-parser.h" #include "template-parser.h" #include "renderer.h" +const char* +blogc_get_variable(const char *name, b_trie_t *global, b_trie_t *local) +{ + const char *rv = NULL; + if (local != NULL) { + rv = b_trie_lookup(local, name); + if (rv != NULL) + return rv; + } + if (global != NULL) + rv = b_trie_lookup(global, name); + return rv; +} + + +char* +blogc_format_date(b_trie_t *global, b_trie_t *local) +{ + const char *date = blogc_get_variable("DATE", global, local); + const char *date_format = blogc_get_variable("DATE_FORMAT", global, local); + if (date == NULL) + return NULL; + if (date_format == NULL) + return b_strdup(date); +#ifdef HAVE_TIME_H + struct tm tm; + memset(&tm, 0, sizeof(struct tm)); + if (NULL == strptime(date, "%Y-%m-%d %H:%M:%S", &tm)) { + fprintf(stderr, "blogc: warning: Failed to parse DATE variable: %s\n", + date); + return b_strdup(date); + } + char tmp[1024]; + if (0 == strftime(tmp, sizeof(tmp), date_format, &tm)) { + fprintf(stderr, "blogc: warning: Failed to format DATE variable, " + "FORMAT is too long: %s\n", date_format); + return b_strdup(date); + } + return b_strdup(tmp); +#else + fprintf(stderr, "blogc: warning: Can't pre-process DATE variable.\n"); + return NULL; +#endif +} + + char* blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing) { @@ -29,7 +81,8 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing b_string_t *str = b_string_new(); b_trie_t *tmp_source = NULL; - char *config_value = NULL; + const char *config_value = NULL; + char *config_value2 = NULL; unsigned int if_count = 0; unsigned int if_skip = 0; @@ -88,23 +141,19 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing case BLOGC_TEMPLATE_VARIABLE_STMT: if (stmt->value != NULL) { - - // try local config first - if (tmp_source != NULL) { - config_value = b_trie_lookup(tmp_source, stmt->value); - if (config_value != NULL) { - b_string_append(str, config_value); + if (0 == strcmp(stmt->value, "DATE_FORMATTED")) { + config_value2 = blogc_format_date(config, tmp_source); + if (config_value2 != NULL) { + b_string_append(str, config_value2); + free(config_value2); + config_value2 = NULL; break; } } - - // if not found, try global config - if (config != NULL) { - config_value = b_trie_lookup(config, stmt->value); - if (config_value != NULL) { + else { + config_value = blogc_get_variable(stmt->value, config, tmp_source); + if (config_value != NULL) b_string_append(str, config_value); - break; - } } } break; @@ -127,10 +176,16 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing case BLOGC_TEMPLATE_IF_STMT: defined = false; if (stmt->value != NULL) { - if (tmp_source != NULL && b_trie_lookup(tmp_source, stmt->value) != NULL) - defined = true; - if (config != NULL && b_trie_lookup(config, stmt->value) != NULL) - defined = true; + if (0 == strcmp(stmt->value, "DATE_FORMATTED")) { + config_value2 = blogc_format_date(config, tmp_source); + if (config_value2 != NULL) { + defined = true; + free(config_value2); + config_value2 = NULL; + } + } + else + defined = blogc_get_variable(stmt->value, config, tmp_source) != NULL; } if ((!if_not && !defined) || (if_not && defined)) { if_skip = if_count; diff --git a/src/renderer.h b/src/renderer.h index c9a0ed1..e4ad4a1 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -12,6 +12,8 @@ #include #include "utils/utils.h" +const char* blogc_get_variable(const char *name, b_trie_t *global, b_trie_t *local); +char* blogc_format_date(b_trie_t *global, b_trie_t *local); char* blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing); diff --git a/tests/check_renderer.c b/tests/check_renderer.c index f8acc14..681b821 100644 --- a/tests/check_renderer.c +++ b/tests/check_renderer.c @@ -28,10 +28,13 @@ create_sources(unsigned int count) const char *s[] = { "BOLA: asd\n" "GUDA: zxc\n" + "DATE: 2015-01-02 03:04:05\n" + "DATE_FORMAT: %R\n" "-----\n" "ahahahahahahahaha", "BOLA: asd2\n" "GUDA: zxc2\n" + "DATE: 2014-02-03 04:05:06\n" "-----\n" "ahahahahahahahaha2", "BOLA: asd3\n" @@ -58,6 +61,8 @@ test_render_entry(void **state) "foo\n" "{% block listing_once %}fuuu{% endblock %}\n" "{% block entry %}\n" + "{{ DATE }}\n" + "{% if DATE_FORMATTED %}{{ DATE_FORMATTED }}{% endif %}\n" "{% if GUDA %}{{ GUDA }}{% endif %}\n" "{% if CHUNDA %}{{ CHUNDA }}{% endif %}\n" "{% endblock %}\n" @@ -73,6 +78,8 @@ test_render_entry(void **state) "foo\n" "\n" "\n" + "2015-01-02 03:04:05\n" + "03:04\n" "zxc\n" "\n" "\n" @@ -94,6 +101,7 @@ test_render_listing(void **state) "{% if CHUNDA %}{{ CHUNDA }}{% endif %}\n" "{% endblock %}\n" "{% block listing %}\n" + "{% if DATE_FORMATTED %}{{ DATE_FORMATTED }}{% endif %}\n" "bola: {% if BOLA %}{{ BOLA }}{% endif %}\n" "{% endblock %}\n"; blogc_error_t *err = NULL; @@ -108,10 +116,13 @@ test_render_listing(void **state) "fuuu\n" "\n" "\n" + "03:04\n" "bola: asd\n" "\n" + "2014-02-03 04:05:06\n" "bola: asd2\n" "\n" + "\n" "bola: asd3\n" "\n"); blogc_template_free_stmts(l); @@ -319,6 +330,109 @@ test_render_prefer_local_variable(void **state) } +static void +test_get_variable(void **state) +{ + b_trie_t *g = b_trie_new(free); + b_trie_insert(g, "NAME", b_strdup("bola")); + b_trie_insert(g, "TITLE", b_strdup("bola2")); + b_trie_t *l = b_trie_new(free); + b_trie_insert(l, "NAME", b_strdup("chunda")); + b_trie_insert(l, "TITLE", b_strdup("chunda2")); + assert_string_equal(blogc_get_variable("NAME", g, l), "chunda"); + assert_string_equal(blogc_get_variable("TITLE", g, l), "chunda2"); + assert_null(blogc_get_variable("BOLA", g, l)); + b_trie_free(g); + b_trie_free(l); +} + + +static void +test_get_variable_only_local(void **state) +{ + b_trie_t *g = NULL; + b_trie_t *l = b_trie_new(free); + b_trie_insert(l, "NAME", b_strdup("chunda")); + b_trie_insert(l, "TITLE", b_strdup("chunda2")); + assert_string_equal(blogc_get_variable("NAME", g, l), "chunda"); + assert_string_equal(blogc_get_variable("TITLE", g, l), "chunda2"); + assert_null(blogc_get_variable("BOLA", g, l)); + b_trie_free(l); +} + + +static void +test_get_variable_only_global(void **state) +{ + b_trie_t *g = b_trie_new(free); + b_trie_insert(g, "NAME", b_strdup("bola")); + b_trie_insert(g, "TITLE", b_strdup("bola2")); + b_trie_t *l = NULL; + assert_string_equal(blogc_get_variable("NAME", g, l), "bola"); + assert_string_equal(blogc_get_variable("TITLE", g, l), "bola2"); + assert_null(blogc_get_variable("BOLA", g, l)); + b_trie_free(g); +} + + +static void +test_format_date(void **state) +{ + b_trie_t *g = b_trie_new(free); + b_trie_insert(g, "DATE_FORMAT", b_strdup("%H -- %j")); + b_trie_t *l = b_trie_new(free); + b_trie_insert(l, "DATE", b_strdup("2015-01-02 03:04:05")); + b_trie_insert(l, "DATE_FORMAT", b_strdup("%R")); + char *date = blogc_format_date(g, l); + assert_string_equal(date, "03:04"); + free(date); + b_trie_free(g); + b_trie_free(l); +} + + +static void +test_format_date_with_global_format(void **state) +{ + b_trie_t *g = b_trie_new(free); + b_trie_insert(g, "DATE_FORMAT", b_strdup("%H -- %j")); + b_trie_t *l = b_trie_new(free); + b_trie_insert(l, "DATE", b_strdup("2015-01-02 03:04:05")); + char *date = blogc_format_date(g, l); + assert_string_equal(date, "03 -- 002"); + free(date); + b_trie_free(g); + b_trie_free(l); +} + + +static void +test_format_date_without_format(void **state) +{ + b_trie_t *g = b_trie_new(free); + b_trie_t *l = b_trie_new(free); + b_trie_insert(l, "DATE", b_strdup("2015-01-02 03:04:05")); + char *date = blogc_format_date(g, l); + assert_string_equal(date, "2015-01-02 03:04:05"); + free(date); + b_trie_free(g); + b_trie_free(l); +} + + +static void +test_format_date_without_date(void **state) +{ + b_trie_t *g = b_trie_new(free); + b_trie_t *l = b_trie_new(free); + char *date = blogc_format_date(g, l); + assert_null(date); + free(date); + b_trie_free(g); + b_trie_free(l); +} + + int main(void) { @@ -332,6 +446,13 @@ main(void) unit_test(test_render_outside_block), unit_test(test_render_null), unit_test(test_render_prefer_local_variable), + unit_test(test_get_variable), + unit_test(test_get_variable_only_local), + unit_test(test_get_variable_only_global), + unit_test(test_format_date), + unit_test(test_format_date_with_global_format), + unit_test(test_format_date_without_format), + unit_test(test_format_date_without_date), }; return run_tests(tests); } -- cgit v1.2.3-18-g5258