diff options
| -rw-r--r-- | README.md | 6 | ||||
| -rw-r--r-- | configure.ac | 4 | ||||
| -rw-r--r-- | src/main.c | 8 | ||||
| -rw-r--r-- | src/renderer.c | 91 | ||||
| -rw-r--r-- | src/renderer.h | 2 | ||||
| -rw-r--r-- | tests/check_renderer.c | 121 | 
6 files changed, 208 insertions, 24 deletions
| @@ -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.          <h1>My cool blog</h1>          {% block entry %}          <h2>{{ TITLE }}</h2> -        {% if DATE %}<h4>Published in: {{ DATE }}</h4>{% endif %} +        {% if DATE_FORMATTED %}<h4>Published in: {{ DATE_FORMATTED }}</h4>{% endif %}          <pre>{{ CONTENT }}</pre>          {% endblock %}          {% block listing_once %}<ul>{% endblock %} -        {% block listing %}<p><a href="{{ FILENAME }}.html">{{ TITLE }}</a>{% if DATE %} - {{ DATE }}{% endif %}</p>{% endblock %} +        {% block listing %}<p><a href="{{ FILENAME }}.html">{{ TITLE }}</a>{% if DATE_FORMATTED %} - {{ DATE_FORMATTED }}{% endif %}</p>{% endblock %}          {% block listing_once %}</ul>{% endblock %}      </body>  </html> 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 @@ -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 <config.h>  #endif /* HAVE_CONFIG_H */ +#ifdef HAVE_TIME_H +#include <time.h> +#endif /* HAVE_TIME_H */ + +#include <stdio.h>  #include <string.h>  #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 <stdbool.h>  #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);  } | 
