aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md6
-rw-r--r--configure.ac4
-rw-r--r--src/main.c8
-rw-r--r--src/renderer.c91
-rw-r--r--src/renderer.h2
-rw-r--r--tests/check_renderer.c121
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.
<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
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 <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);
}