From 8d96c02e5118cf7bd656fde9100570a67115d19a Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Tue, 29 Dec 2015 00:12:51 +0100 Subject: man: renderer: template-parser: added foreach iterator support --- man/blogc-template.7.ronn | 38 +++++++++++++ src/renderer.c | 71 +++++++++++++++++++++++++ src/renderer.h | 2 + src/template-parser.c | 67 ++++++++++++++++++++++- src/template-parser.h | 2 + tests/check_renderer.c | 70 +++++++++++++++++++++++- tests/check_template_parser.c | 121 +++++++++++++++++++++++++++++++++++++++--- 7 files changed, 361 insertions(+), 10 deletions(-) diff --git a/man/blogc-template.7.ronn b/man/blogc-template.7.ronn index 77073af..46413c2 100644 --- a/man/blogc-template.7.ronn +++ b/man/blogc-template.7.ronn @@ -163,6 +163,44 @@ Or: Title is the default title {% endif %} +## TEMPLATE ITERATORS + +Template iterators are used to iterate over the value of a variable, that is handled +as a list. + +The available conditionals are: `foreach`. + +### foreach iterator + +The content of a `foreach` iterator is included in the output file when the target +variable is defined, and is repeated for each item in the list parsed from the variable +value. + +The variable value should be formatted as a comma-separated list of items. Quotes are +not supported, as this is intended to work with identifiers, like slugs, and not with +arbitrary strings. + +Spaces and tabs before and after list items are stripped for consistency. + +This is how a variable would be formatted: + + item1, item2, item3 + +For more info about how to define variables, see blogc(1) and blogc-source(7). + +This is how a `foreach` iterator is defined in a template: + + {% foreach TAGS %} + {{ FOREACH_ITEM }} + {% endforeach %} + +Where `TAGS` is the variable with comma-separated list of items, and `FOREACH_ITEM` +is the variable defined by blogc(1), that will store the item value for a given +iteration. + +If the value of the `TAGS` variable is "item1, item2, item3", this template would be +rendered as 3 times, one for each item value. + ## BUGS The template content is handled by handwritten parsers, that even being well diff --git a/src/renderer.c b/src/renderer.c index 3061c43..6cf924e 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -88,6 +88,24 @@ blogc_format_variable(const char *name, b_trie_t *global, b_trie_t *local) } +b_slist_t* +blogc_split_list_variable(const char *name, b_trie_t *global, b_trie_t *local) +{ + const char *value = blogc_get_variable(name, global, local); + if (value == NULL) + return NULL; + + b_slist_t *rv = NULL; + + char **tmp = b_str_split(value, ',', 0); + for (unsigned int i = 0; tmp[i] != NULL; i++) + rv = b_slist_append(rv, b_strdup(b_str_strip(tmp[i]))); + b_strv_free(tmp); + + return rv; +} + + char* blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing) { @@ -106,6 +124,10 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing unsigned int if_count = 0; unsigned int if_skip = 0; + b_slist_t *foreach_var = NULL; + b_slist_t *foreach_var_start = NULL; + b_slist_t *foreach_start = NULL; + bool if_not = false; bool inside_block = false; bool evaluate = false; @@ -174,6 +196,11 @@ 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) { + if (0 == strcmp(stmt->value, "FOREACH_ITEM")) { // foreach + if (foreach_var != NULL && foreach_var->data != NULL) + b_string_append(str, foreach_var->data); + break; + } config_value = blogc_format_variable(stmt->value, config, inside_block ? tmp_source : NULL); if (config_value != NULL) { @@ -281,9 +308,53 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing case BLOGC_TEMPLATE_ENDIF_STMT: if_count--; break; + + case BLOGC_TEMPLATE_FOREACH_STMT: + if (foreach_var_start == NULL) { + if (stmt->value != NULL) + foreach_var_start = blogc_split_list_variable(stmt->value, + config, inside_block ? tmp_source : NULL); + + if (foreach_var_start != NULL) { + foreach_var = foreach_var_start; + foreach_start = tmp; + } + else { + + // we can just skip anything and walk until the next + // 'endforeach' + while (stmt->type != BLOGC_TEMPLATE_ENDFOREACH_STMT) { + tmp = tmp->next; + stmt = tmp->data; + } + break; + } + } + + if (foreach_var == NULL) { + foreach_start = tmp; + foreach_var = foreach_var_start; + } + break; + + case BLOGC_TEMPLATE_ENDFOREACH_STMT: + if (foreach_start != NULL && foreach_var != NULL) { + foreach_var = foreach_var->next; + if (foreach_var != NULL) { + tmp = foreach_start; + continue; + } + } + foreach_start = NULL; + b_slist_free_full(foreach_var_start, free); + foreach_var_start = NULL; + break; } tmp = tmp->next; } + // no need to free temporary variables here. the template parser makes sure + // that templates are sane and statements are closed. + return b_string_free(str, false); } diff --git a/src/renderer.h b/src/renderer.h index e5cff6e..de26e98 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -15,6 +15,8 @@ const char* blogc_get_variable(const char *name, b_trie_t *global, b_trie_t *local); char* blogc_format_date(const char *date, b_trie_t *global, b_trie_t *local); char* blogc_format_variable(const char *name, b_trie_t *global, b_trie_t *local); +b_slist_t* blogc_split_list_variable(const char *name, 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/src/template-parser.c b/src/template-parser.c index f6912df..e5c750e 100644 --- a/src/template-parser.c +++ b/src/template-parser.c @@ -32,6 +32,8 @@ typedef enum { TEMPLATE_BLOCK_IF_OPERAND_START, TEMPLATE_BLOCK_IF_STRING_OPERAND, TEMPLATE_BLOCK_IF_VARIABLE_OPERAND, + TEMPLATE_BLOCK_FOREACH_START, + TEMPLATE_BLOCK_FOREACH_VARIABLE, TEMPLATE_BLOCK_END, TEMPLATE_VARIABLE_START, TEMPLATE_VARIABLE, @@ -65,6 +67,7 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) blogc_template_stmt_operator_t tmp_op = 0; unsigned int if_count = 0; + bool foreach_open = false; b_slist_t *stmts = NULL; blogc_template_stmt_t *stmt = NULL; @@ -201,11 +204,42 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) "statement."); break; } + else if ((current - start == 7) && + (0 == strncmp("foreach", src + start, 7))) + { + if (!foreach_open) { + state = TEMPLATE_BLOCK_FOREACH_START; + type = BLOGC_TEMPLATE_FOREACH_STMT; + start = current; + foreach_open = true; + break; + } + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, + src, src_len, current, "'foreach' statements can't " + "be nested."); + break; + } + else if ((current - start == 10) && + (0 == strncmp("endforeach", src + start, 10))) + { + if (foreach_open) { + state = TEMPLATE_BLOCK_END; + type = BLOGC_TEMPLATE_ENDFOREACH_STMT; + foreach_open = false; + break; + } + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, + src, src_len, current, + "'endforeach' statement without an open 'foreach' " + "statement."); + break; + } } *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, src_len, current, "Invalid statement type: Allowed types are: 'block', " - "'endblock', 'ifdef', 'ifndef' and 'endif'."); + "'endblock', 'ifdef', 'ifndef', 'endif', 'foreach' and " + "'endforeach'."); break; case TEMPLATE_BLOCK_BLOCK_TYPE_START: @@ -337,6 +371,34 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) end2 = current; break; + case TEMPLATE_BLOCK_FOREACH_START: + if (c == ' ') + break; + if (c >= 'A' && c <= 'Z') { + state = TEMPLATE_BLOCK_FOREACH_VARIABLE; + start = current; + break; + } + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, + src_len, current, + "Invalid foreach variable name. Must begin with uppercase " + "letter."); + break; + + case TEMPLATE_BLOCK_FOREACH_VARIABLE: + if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_') + break; + if (c == ' ') { + end = current; + state = TEMPLATE_BLOCK_END; + break; + } + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, + src_len, current, + "Invalid foreach variable name. Must be uppercase letter, " + "number or '_'."); + break; + case TEMPLATE_BLOCK_END: if (c == ' ') break; @@ -469,6 +531,9 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) else if (block_state != BLOCK_CLOSED) *err = blogc_error_new(BLOGC_ERROR_TEMPLATE_PARSER, "An open block was not closed!"); + else if (foreach_open) + *err = blogc_error_new(BLOGC_ERROR_TEMPLATE_PARSER, + "An open 'foreach' statement was not closed!"); } if (*err != NULL) { diff --git a/src/template-parser.h b/src/template-parser.h index d1e9bd6..9da5fff 100644 --- a/src/template-parser.h +++ b/src/template-parser.h @@ -17,6 +17,8 @@ typedef enum { BLOGC_TEMPLATE_IFNDEF_STMT, BLOGC_TEMPLATE_IF_STMT, BLOGC_TEMPLATE_ENDIF_STMT, + BLOGC_TEMPLATE_FOREACH_STMT, + BLOGC_TEMPLATE_ENDFOREACH_STMT, BLOGC_TEMPLATE_BLOCK_STMT, BLOGC_TEMPLATE_ENDBLOCK_STMT, BLOGC_TEMPLATE_VARIABLE_STMT, diff --git a/tests/check_renderer.c b/tests/check_renderer.c index cb8f8f1..cc04fdc 100644 --- a/tests/check_renderer.c +++ b/tests/check_renderer.c @@ -31,6 +31,7 @@ create_sources(unsigned int count) "GUDA2: zxc\n" "DATE: 2015-01-02 03:04:05\n" "DATE_FORMAT: %R\n" + "TAGS: foo, bar,baz\n" "-----\n" "ahahahahahahahaha", "BOLA: asd2\n" @@ -74,7 +75,8 @@ test_render_entry(void **state) "{% if GUDA != \"bola\" %}HEHE{% endif %}\n" "{% if GUDA < \"zxd\" %}LOL2{% endif %}\n" "{% if GUDA > \"zxd\" %}LOL3{% endif %}\n" - "{% if GUDA <= \"zxc\" %}LOL4{% endif %}\n"; + "{% if GUDA <= \"zxc\" %}LOL4{% endif %}\n" + "{% foreach TAGS %}lol {{ FOREACH_ITEM }} haha {% endforeach %}\n"; blogc_error_t *err = NULL; b_slist_t *l = blogc_template_parse(str, strlen(str), &err); assert_non_null(l); @@ -97,7 +99,8 @@ test_render_entry(void **state) "HEHE\n" "LOL2\n" "\n" - "LOL4\n"); + "LOL4\n" + "lol foo haha lol bar haha lol baz haha \n"); blogc_template_free_stmts(l); b_slist_free_full(s, (b_free_func_t) b_trie_free); free(out); @@ -117,6 +120,7 @@ test_render_listing(void **state) "{% block listing %}\n" "{% ifdef DATE_FORMATTED %}{{ DATE_FORMATTED }}{% endif %}\n" "bola: {% ifdef BOLA %}{{ BOLA }}{% endif %}\n" + "{% foreach TAGS %}lol {{ FOREACH_ITEM }} haha {% endforeach %}\n" "{% endblock %}\n"; blogc_error_t *err = NULL; b_slist_t *l = blogc_template_parse(str, strlen(str), &err); @@ -132,12 +136,15 @@ test_render_listing(void **state) "\n" "03:04\n" "bola: asd\n" + "lol foo haha lol bar haha lol baz haha \n" "\n" "2014-02-03 04:05:06\n" "bola: asd2\n" "\n" + "\n" "2013-01-02 03:04:05\n" "bola: asd3\n" + "\n" "\n"); blogc_template_free_stmts(l); b_slist_free_full(s, (b_free_func_t) b_trie_free); @@ -158,6 +165,7 @@ test_render_listing_empty(void **state) "{% block listing %}\n" "{% ifdef DATE_FORMATTED %}{{ DATE_FORMATTED }}{% endif %}\n" "bola: {% ifdef BOLA %}{{ BOLA }}{% endif %}\n" + "{% foreach TAGS %}lol {{ FOREACH_ITEM }} haha {% endforeach %}\n" "{% endblock %}\n"; blogc_error_t *err = NULL; b_slist_t *l = blogc_template_parse(str, strlen(str), &err); @@ -518,6 +526,30 @@ test_render_if_gt_eq(void **state) } +static void +test_render_foreach(void **state) +{ + const char *str = + "{% block entry %}\n" + "{% foreach TAGS %} {{ FOREACH_ITEM }} {% endforeach %}\n" + "{% endblock %}\n"; + blogc_error_t *err = NULL; + b_slist_t *l = blogc_template_parse(str, strlen(str), &err); + assert_non_null(l); + assert_null(err); + b_slist_t *s = create_sources(1); + assert_non_null(s); + char *out = blogc_render(l, s, NULL, false); + assert_string_equal(out, + "\n" + " foo bar baz \n" + "\n"); + blogc_template_free_stmts(l); + b_slist_free_full(s, (b_free_func_t) b_trie_free); + free(out); +} + + static void test_render_null(void **state) { @@ -760,6 +792,37 @@ test_format_variable_with_date(void **state) } +static void +test_split_list_variable(void **state) +{ + b_trie_t *g = b_trie_new(free); + b_trie_insert(g, "TAGS", b_strdup("asd, lol,hehe")); + b_trie_t *l = b_trie_new(free); + b_trie_insert(l, "TAGS", b_strdup("asd, lol,XD")); + b_slist_t *tmp = blogc_split_list_variable("TAGS", g, l); + assert_string_equal(tmp->data, "asd"); + assert_string_equal(tmp->next->data, "lol"); + assert_string_equal(tmp->next->next->data, "XD"); + b_slist_free_full(tmp, free); + b_trie_free(g); + b_trie_free(l); +} + + +static void +test_split_list_variable_not_found(void **state) +{ + b_trie_t *g = b_trie_new(free); + b_trie_insert(g, "TAGS", b_strdup("asd, lol,hehe")); + b_trie_t *l = b_trie_new(free); + b_trie_insert(l, "TAGS", b_strdup("asd, lol,XD")); + b_slist_t *tmp = blogc_split_list_variable("TAG", g, l); + assert_null(tmp); + b_trie_free(g); + b_trie_free(l); +} + + int main(void) { @@ -777,6 +840,7 @@ main(void) unit_test(test_render_if_gt), unit_test(test_render_if_lt_eq), unit_test(test_render_if_gt_eq), + unit_test(test_render_foreach), unit_test(test_render_null), unit_test(test_render_outside_block), unit_test(test_render_prefer_local_variable), @@ -790,6 +854,8 @@ main(void) unit_test(test_format_date_without_date), unit_test(test_format_variable), unit_test(test_format_variable_with_date), + unit_test(test_split_list_variable), + unit_test(test_split_list_variable_not_found), }; return run_tests(tests); } diff --git a/tests/check_template_parser.c b/tests/check_template_parser.c index b712f22..6aaceed 100644 --- a/tests/check_template_parser.c +++ b/tests/check_template_parser.c @@ -61,6 +61,7 @@ test_template_parse(void **state) "{% endblock %}\n" "{% block listing %}{{ BOLA }}{% endblock %}\n" "{% block listing_once %}asd{% endblock %}\n" + "{% foreach BOLA %}hahaha{% endforeach %}\n" "{% if BOLA == \"1\\\"0\" %}aee{% endif %}"; blogc_error_t *err = NULL; b_slist_t *stmts = blogc_template_parse(a, strlen(a), &err); @@ -106,11 +107,20 @@ test_template_parse(void **state) blogc_assert_template_stmt(tmp->next->next->next->next->next->next->next->next->next, "\n", BLOGC_TEMPLATE_CONTENT_STMT); tmp = tmp->next->next->next->next->next->next->next->next->next->next; - blogc_assert_template_if_stmt(tmp, "BOLA", BLOGC_TEMPLATE_OP_EQ, "\"1\\\"0\""); - blogc_assert_template_stmt(tmp->next, "aee", BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(tmp, "BOLA", BLOGC_TEMPLATE_FOREACH_STMT); + blogc_assert_template_stmt(tmp->next, "hahaha", + BLOGC_TEMPLATE_CONTENT_STMT); blogc_assert_template_stmt(tmp->next->next, NULL, + BLOGC_TEMPLATE_ENDFOREACH_STMT); + blogc_assert_template_stmt(tmp->next->next->next, "\n", + BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_if_stmt(tmp->next->next->next->next, "BOLA", + BLOGC_TEMPLATE_OP_EQ, "\"1\\\"0\""); + blogc_assert_template_stmt(tmp->next->next->next->next->next, "aee", + BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(tmp->next->next->next->next->next->next, NULL, BLOGC_TEMPLATE_ENDIF_STMT); - assert_null(tmp->next->next->next); + assert_null(tmp->next->next->next->next->next->next->next); blogc_template_free_stmts(stmts); } @@ -131,6 +141,7 @@ test_template_parse_crlf(void **state) "{% endblock %}\r\n" "{% block listing %}{{ BOLA }}{% endblock %}\r\n" "{% block listing_once %}asd{% endblock %}\r\n" + "{% foreach BOLA %}hahaha{% endforeach %}\r\n" "{% if BOLA == \"1\\\"0\" %}aee{% endif %}"; blogc_error_t *err = NULL; b_slist_t *stmts = blogc_template_parse(a, strlen(a), &err); @@ -176,11 +187,20 @@ test_template_parse_crlf(void **state) blogc_assert_template_stmt(tmp->next->next->next->next->next->next->next->next->next, "\r\n", BLOGC_TEMPLATE_CONTENT_STMT); tmp = tmp->next->next->next->next->next->next->next->next->next->next; - blogc_assert_template_if_stmt(tmp, "BOLA", BLOGC_TEMPLATE_OP_EQ, "\"1\\\"0\""); - blogc_assert_template_stmt(tmp->next, "aee", BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(tmp, "BOLA", BLOGC_TEMPLATE_FOREACH_STMT); + blogc_assert_template_stmt(tmp->next, "hahaha", + BLOGC_TEMPLATE_CONTENT_STMT); blogc_assert_template_stmt(tmp->next->next, NULL, + BLOGC_TEMPLATE_ENDFOREACH_STMT); + blogc_assert_template_stmt(tmp->next->next->next, "\r\n", + BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_if_stmt(tmp->next->next->next->next, "BOLA", + BLOGC_TEMPLATE_OP_EQ, "\"1\\\"0\""); + blogc_assert_template_stmt(tmp->next->next->next->next->next, "aee", + BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(tmp->next->next->next->next->next->next, NULL, BLOGC_TEMPLATE_ENDIF_STMT); - assert_null(tmp->next->next->next); + assert_null(tmp->next->next->next->next->next->next->next); blogc_template_free_stmts(stmts); } @@ -382,6 +402,24 @@ test_template_parse_invalid_block_nested(void **state) } +static void +test_template_parse_invalid_foreach_nested(void **state) +{ + const char *a = + "{% foreach A %}\n" + "{% foreach B %}\n"; + blogc_error_t *err = NULL; + b_slist_t *stmts = blogc_template_parse(a, strlen(a), &err); + assert_non_null(err); + assert_null(stmts); + assert_int_equal(err->type, BLOGC_ERROR_TEMPLATE_PARSER); + assert_string_equal(err->msg, + "'foreach' statements can't be nested.\n" + "Error occurred near line 2, position 11: {% foreach B %}"); + blogc_error_free(err); +} + + static void test_template_parse_invalid_block_not_open(void **state) { @@ -415,6 +453,22 @@ test_template_parse_invalid_endif_not_open(void **state) } +static void +test_template_parse_invalid_endforeach_not_open(void **state) +{ + const char *a = "{% endforeach %}\n"; + blogc_error_t *err = NULL; + b_slist_t *stmts = blogc_template_parse(a, strlen(a), &err); + assert_non_null(err); + assert_null(stmts); + assert_int_equal(err->type, BLOGC_ERROR_TEMPLATE_PARSER); + assert_string_equal(err->msg, + "'endforeach' statement without an open 'foreach' statement.\n" + "Error occurred near line 1, position 14: {% endforeach %}"); + blogc_error_free(err); +} + + static void test_template_parse_invalid_block_name(void **state) { @@ -426,7 +480,7 @@ test_template_parse_invalid_block_name(void **state) assert_int_equal(err->type, BLOGC_ERROR_TEMPLATE_PARSER); assert_string_equal(err->msg, "Invalid statement type: Allowed types are: 'block', 'endblock', 'ifdef', " - "'ifndef' and 'endif'.\n" + "'ifndef', 'endif', 'foreach' and 'endforeach'.\n" "Error occurred near line 1, position 10: {% chunda %}"); blogc_error_free(err); } @@ -481,6 +535,23 @@ test_template_parse_invalid_ifdef_start(void **state) } +static void +test_template_parse_invalid_foreach_start(void **state) +{ + const char *a = "{% block entry %}{% foreach guda %}\n"; + blogc_error_t *err = NULL; + b_slist_t *stmts = blogc_template_parse(a, strlen(a), &err); + assert_non_null(err); + assert_null(stmts); + assert_int_equal(err->type, BLOGC_ERROR_TEMPLATE_PARSER); + assert_string_equal(err->msg, + "Invalid foreach variable name. Must begin with uppercase letter.\n" + "Error occurred near line 1, position 29: " + "{% block entry %}{% foreach guda %}"); + blogc_error_free(err); +} + + static void test_template_parse_invalid_ifdef_variable(void **state) { @@ -498,6 +569,23 @@ test_template_parse_invalid_ifdef_variable(void **state) } +static void +test_template_parse_invalid_foreach_variable(void **state) +{ + const char *a = "{% block entry %}{% foreach BoLA %}\n"; + blogc_error_t *err = NULL; + b_slist_t *stmts = blogc_template_parse(a, strlen(a), &err); + assert_non_null(err); + assert_null(stmts); + assert_int_equal(err->type, BLOGC_ERROR_TEMPLATE_PARSER); + assert_string_equal(err->msg, + "Invalid foreach variable name. Must be uppercase letter, number or '_'.\n" + "Error occurred near line 1, position 30: " + "{% block entry %}{% foreach BoLA %}"); + blogc_error_free(err); +} + + static void test_template_parse_invalid_if_operator(void **state) { @@ -678,6 +766,20 @@ test_template_parse_invalid_block_not_closed(void **state) } +static void +test_template_parse_invalid_foreach_not_closed(void **state) +{ + const char *a = "{% foreach ASD %}\n"; + blogc_error_t *err = NULL; + b_slist_t *stmts = blogc_template_parse(a, strlen(a), &err); + assert_non_null(err); + assert_null(stmts); + assert_int_equal(err->type, BLOGC_ERROR_TEMPLATE_PARSER); + assert_string_equal(err->msg, "An open 'foreach' statement was not closed!"); + blogc_error_free(err); +} + + int main(void) { @@ -688,13 +790,17 @@ main(void) unit_test(test_template_parse_ifdef_and_var_outside_block), unit_test(test_template_parse_invalid_block_start), unit_test(test_template_parse_invalid_block_nested), + unit_test(test_template_parse_invalid_foreach_nested), unit_test(test_template_parse_invalid_block_not_open), unit_test(test_template_parse_invalid_endif_not_open), + unit_test(test_template_parse_invalid_endforeach_not_open), unit_test(test_template_parse_invalid_block_name), unit_test(test_template_parse_invalid_block_type_start), unit_test(test_template_parse_invalid_block_type), unit_test(test_template_parse_invalid_ifdef_start), + unit_test(test_template_parse_invalid_foreach_start), unit_test(test_template_parse_invalid_ifdef_variable), + unit_test(test_template_parse_invalid_foreach_variable), unit_test(test_template_parse_invalid_if_operator), unit_test(test_template_parse_invalid_if_operand), unit_test(test_template_parse_invalid_if_operand2), @@ -706,6 +812,7 @@ main(void) unit_test(test_template_parse_invalid_close2), unit_test(test_template_parse_invalid_if_not_closed), unit_test(test_template_parse_invalid_block_not_closed), + unit_test(test_template_parse_invalid_foreach_not_closed), }; return run_tests(tests); } -- cgit v1.2.3-18-g5258 From e727bdcde63804a308103adeaa2637c5ee1ebdc8 Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Tue, 29 Dec 2015 00:39:01 +0100 Subject: template-parser: do not accept variables startins with numbers and _ --- src/template-parser.c | 19 +++++------ tests/check_template_parser.c | 74 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 83 insertions(+), 10 deletions(-) diff --git a/src/template-parser.c b/src/template-parser.c index e5c750e..030ecca 100644 --- a/src/template-parser.c +++ b/src/template-parser.c @@ -338,21 +338,22 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) case TEMPLATE_BLOCK_IF_OPERAND_START: if (c == ' ') break; - if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_') { + if (c >= 'A' && c <= 'Z') { state = TEMPLATE_BLOCK_IF_VARIABLE_OPERAND; start2 = current; break; } - if (c != '"') { - op_start = 0; - op_end = 0; - *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, - src_len, current, - "Invalid 'if' operand. Must be double-quoted static string."); + if (c == '"') { + state = TEMPLATE_BLOCK_IF_STRING_OPERAND; + start2 = current; break; } - state = TEMPLATE_BLOCK_IF_STRING_OPERAND; - start2 = current; + op_start = 0; + op_end = 0; + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, + src_len, current, + "Invalid 'if' operand. Must be double-quoted static " + "string or variable."); break; case TEMPLATE_BLOCK_IF_STRING_OPERAND: diff --git a/tests/check_template_parser.c b/tests/check_template_parser.c index 6aaceed..145a27e 100644 --- a/tests/check_template_parser.c +++ b/tests/check_template_parser.c @@ -569,6 +569,23 @@ test_template_parse_invalid_ifdef_variable(void **state) } +static void +test_template_parse_invalid_ifdef_variable2(void **state) +{ + const char *a = "{% block entry %}{% ifdef 0123 %}\n"; + blogc_error_t *err = NULL; + b_slist_t *stmts = blogc_template_parse(a, strlen(a), &err); + assert_non_null(err); + assert_null(stmts); + assert_int_equal(err->type, BLOGC_ERROR_TEMPLATE_PARSER); + assert_string_equal(err->msg, + "Invalid variable name. Must begin with uppercase letter.\n" + "Error occurred near line 1, position 27: " + "{% block entry %}{% ifdef 0123 %}"); + blogc_error_free(err); +} + + static void test_template_parse_invalid_foreach_variable(void **state) { @@ -586,6 +603,23 @@ test_template_parse_invalid_foreach_variable(void **state) } +static void +test_template_parse_invalid_foreach_variable2(void **state) +{ + const char *a = "{% block entry %}{% foreach 0123 %}\n"; + blogc_error_t *err = NULL; + b_slist_t *stmts = blogc_template_parse(a, strlen(a), &err); + assert_non_null(err); + assert_null(stmts); + assert_int_equal(err->type, BLOGC_ERROR_TEMPLATE_PARSER); + assert_string_equal(err->msg, + "Invalid foreach variable name. Must begin with uppercase letter.\n" + "Error occurred near line 1, position 29: {% block entry %}" + "{% foreach 0123 %}"); + blogc_error_free(err); +} + + static void test_template_parse_invalid_if_operator(void **state) { @@ -613,7 +647,7 @@ test_template_parse_invalid_if_operand(void **state) assert_null(stmts); assert_int_equal(err->type, BLOGC_ERROR_TEMPLATE_PARSER); assert_string_equal(err->msg, - "Invalid 'if' operand. Must be double-quoted static string.\n" + "Invalid 'if' operand. Must be double-quoted static string or variable.\n" "Error occurred near line 1, position 32: " "{% block entry %}{% if BOLA == asd %}"); blogc_error_free(err); @@ -637,6 +671,23 @@ test_template_parse_invalid_if_operand2(void **state) } +static void +test_template_parse_invalid_if_operand3(void **state) +{ + const char *a = "{% block entry %}{% if BOLA == 0123 %}\n"; + blogc_error_t *err = NULL; + b_slist_t *stmts = blogc_template_parse(a, strlen(a), &err); + assert_non_null(err); + assert_null(stmts); + assert_int_equal(err->type, BLOGC_ERROR_TEMPLATE_PARSER); + assert_string_equal(err->msg, + "Invalid 'if' operand. Must be double-quoted static string or variable.\n" + "Error occurred near line 1, position 32: " + "{% block entry %}{% if BOLA == 0123 %}"); + blogc_error_free(err); +} + + static void test_template_parse_invalid_block_end(void **state) { @@ -687,6 +738,23 @@ test_template_parse_invalid_variable_name2(void **state) } +static void +test_template_parse_invalid_variable_name3(void **state) +{ + const char *a = "{% block entry %}{{ 0123 }}{% endblock %}\n"; + blogc_error_t *err = NULL; + b_slist_t *stmts = blogc_template_parse(a, strlen(a), &err); + assert_non_null(err); + assert_null(stmts); + assert_int_equal(err->type, BLOGC_ERROR_TEMPLATE_PARSER); + assert_string_equal(err->msg, + "Invalid variable name. Must begin with uppercase letter.\n" + "Error occurred near line 1, position 21: {% block entry %}{{ 0123 }}" + "{% endblock %}"); + blogc_error_free(err); +} + + static void test_template_parse_invalid_variable_end(void **state) { @@ -800,13 +868,17 @@ main(void) unit_test(test_template_parse_invalid_ifdef_start), unit_test(test_template_parse_invalid_foreach_start), unit_test(test_template_parse_invalid_ifdef_variable), + unit_test(test_template_parse_invalid_ifdef_variable2), unit_test(test_template_parse_invalid_foreach_variable), + unit_test(test_template_parse_invalid_foreach_variable2), unit_test(test_template_parse_invalid_if_operator), unit_test(test_template_parse_invalid_if_operand), unit_test(test_template_parse_invalid_if_operand2), + unit_test(test_template_parse_invalid_if_operand3), unit_test(test_template_parse_invalid_block_end), unit_test(test_template_parse_invalid_variable_name), unit_test(test_template_parse_invalid_variable_name2), + unit_test(test_template_parse_invalid_variable_name3), unit_test(test_template_parse_invalid_variable_end), unit_test(test_template_parse_invalid_close), unit_test(test_template_parse_invalid_close2), -- cgit v1.2.3-18-g5258 From 3d24a8847e156804e19515ddeefd3912402515be Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Tue, 29 Dec 2015 01:46:50 +0100 Subject: renderer: foreach variables should be splitted in spaces rather than commas --- man/blogc-template.7.ronn | 14 ++++++-------- src/renderer.c | 12 ++++++++---- tests/check_renderer.c | 10 +++++----- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/man/blogc-template.7.ronn b/man/blogc-template.7.ronn index 46413c2..2875e36 100644 --- a/man/blogc-template.7.ronn +++ b/man/blogc-template.7.ronn @@ -176,15 +176,13 @@ The content of a `foreach` iterator is included in the output file when the targ variable is defined, and is repeated for each item in the list parsed from the variable value. -The variable value should be formatted as a comma-separated list of items. Quotes are +The variable value should be formatted as a space-separated list of items. Quotes are not supported, as this is intended to work with identifiers, like slugs, and not with arbitrary strings. -Spaces and tabs before and after list items are stripped for consistency. +This is how a variable value would be formatted: -This is how a variable would be formatted: - - item1, item2, item3 + item1 item2 item3 For more info about how to define variables, see blogc(1) and blogc-source(7). @@ -194,12 +192,12 @@ This is how a `foreach` iterator is defined in a template: {{ FOREACH_ITEM }} {% endforeach %} -Where `TAGS` is the variable with comma-separated list of items, and `FOREACH_ITEM` +Where `TAGS` is the variable with space-separated list of items, and `FOREACH_ITEM` is the variable defined by blogc(1), that will store the item value for a given iteration. -If the value of the `TAGS` variable is "item1, item2, item3", this template would be -rendered as 3 times, one for each item value. +If the value of the `TAGS` variable is "item1 item2 item3", this template is +rendered 3 times, one for each item value. ## BUGS diff --git a/src/renderer.c b/src/renderer.c index 6cf924e..defbe10 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -97,10 +97,14 @@ blogc_split_list_variable(const char *name, b_trie_t *global, b_trie_t *local) b_slist_t *rv = NULL; - char **tmp = b_str_split(value, ',', 0); - for (unsigned int i = 0; tmp[i] != NULL; i++) - rv = b_slist_append(rv, b_strdup(b_str_strip(tmp[i]))); - b_strv_free(tmp); + char **tmp = b_str_split(value, ' ', 0); + for (unsigned int i = 0; tmp[i] != NULL; i++) { + if (tmp[i][0] != '\0') // ignore empty strings + rv = b_slist_append(rv, tmp[i]); + else + free(tmp[i]); + } + free(tmp); return rv; } diff --git a/tests/check_renderer.c b/tests/check_renderer.c index cc04fdc..e380be7 100644 --- a/tests/check_renderer.c +++ b/tests/check_renderer.c @@ -31,7 +31,7 @@ create_sources(unsigned int count) "GUDA2: zxc\n" "DATE: 2015-01-02 03:04:05\n" "DATE_FORMAT: %R\n" - "TAGS: foo, bar,baz\n" + "TAGS: foo bar baz\n" "-----\n" "ahahahahahahahaha", "BOLA: asd2\n" @@ -796,9 +796,9 @@ static void test_split_list_variable(void **state) { b_trie_t *g = b_trie_new(free); - b_trie_insert(g, "TAGS", b_strdup("asd, lol,hehe")); + b_trie_insert(g, "TAGS", b_strdup("asd lol hehe")); b_trie_t *l = b_trie_new(free); - b_trie_insert(l, "TAGS", b_strdup("asd, lol,XD")); + b_trie_insert(l, "TAGS", b_strdup("asd lol XD")); b_slist_t *tmp = blogc_split_list_variable("TAGS", g, l); assert_string_equal(tmp->data, "asd"); assert_string_equal(tmp->next->data, "lol"); @@ -813,9 +813,9 @@ static void test_split_list_variable_not_found(void **state) { b_trie_t *g = b_trie_new(free); - b_trie_insert(g, "TAGS", b_strdup("asd, lol,hehe")); + b_trie_insert(g, "TAGS", b_strdup("asd lol hehe")); b_trie_t *l = b_trie_new(free); - b_trie_insert(l, "TAGS", b_strdup("asd, lol,XD")); + b_trie_insert(l, "TAGS", b_strdup("asd lol XD")); b_slist_t *tmp = blogc_split_list_variable("TAG", g, l); assert_null(tmp); b_trie_free(g); -- cgit v1.2.3-18-g5258 From 1b851a5778196a34affe397e4c502b326afd3270 Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Tue, 29 Dec 2015 22:32:38 +0100 Subject: loader: parse tags as space-separated string --- src/loader.c | 9 ++++++--- tests/check_loader.c | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/loader.c b/src/loader.c index 8f04dae..5cd24df 100644 --- a/src/loader.c +++ b/src/loader.c @@ -141,11 +141,14 @@ blogc_source_parse_from_files(b_trie_t *conf, b_slist_t *l, blogc_error_t **err) b_trie_free(s); continue; } - char **tags = b_str_split(tags_str, ',', 0); + char **tags = b_str_split(tags_str, ' ', 0); bool found = false; - for (unsigned int i = 0; tags[i] != NULL; i++) - if (0 == strcmp(b_str_strip(tags[i]), filter_tag)) + for (unsigned int i = 0; tags[i] != NULL; i++) { + if (tags[i][0] == '\0') + continue; + if (0 == strcmp(tags[i], filter_tag)) found = true; + } b_strv_free(tags); if (!found) { b_trie_free(s); diff --git a/tests/check_loader.c b/tests/check_loader.c index f5be3e7..03f0ef1 100644 --- a/tests/check_loader.c +++ b/tests/check_loader.c @@ -478,7 +478,7 @@ test_source_parse_from_files_filter_by_page_and_tag(void **state) will_return(__wrap_blogc_file_get_contents, b_strdup( "ASD: 789\n" "DATE: 2003-02-03 04:05:06\n" - "TAGS: chunda, bola\n" + "TAGS: chunda bola\n" "--------\n" "bola")); will_return(__wrap_blogc_file_get_contents, "bola4.txt"); @@ -505,7 +505,7 @@ test_source_parse_from_files_filter_by_page_and_tag(void **state) will_return(__wrap_blogc_file_get_contents, b_strdup( "ASD: 7894\n" "DATE: 2007-02-03 04:05:06\n" - "TAGS: yay, chunda\n" + "TAGS: yay chunda\n" "--------\n" "bola")); blogc_error_t *err = NULL; -- cgit v1.2.3-18-g5258 From be71d0c966e558555ccdc25539e7f9be2ca1693b Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Wed, 30 Dec 2015 01:53:31 +0100 Subject: man: fix blogc-template man page --- man/blogc-template.7.ronn | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/man/blogc-template.7.ronn b/man/blogc-template.7.ronn index 2875e36..3a665ab 100644 --- a/man/blogc-template.7.ronn +++ b/man/blogc-template.7.ronn @@ -4,8 +4,8 @@ blogc-template(7) -- blogc's template format ## DESCRIPTION Template files are used as base to build output files by blogc(1). These files -can include variables, blocks and conditionals, that will directly affect the -output files. +can include variables, blocks, conditionals and iterators, that will directly +affect the output files. The syntax of the template files is defined to be simple, without affecting the content output. The syntax is somewhat inspired by Jinja2 syntax. -- cgit v1.2.3-18-g5258 From 02f4db9ceaff5ebd64122c958c4d6746191e9308 Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Fri, 1 Jan 2016 19:55:04 +0100 Subject: build: do not use AM_PROG_AR, as it is useless for us --- configure.ac | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 3cfb07d..43a336c 100644 --- a/configure.ac +++ b/configure.ac @@ -4,15 +4,13 @@ AC_INIT([blogc], [0.5.1], [https://github.com/blogc/blogc], [blogc], [https://bl AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) -AM_INIT_AUTOMAKE([1.13 foreign dist-bzip2 dist-xz dist-zip subdir-objects serial-tests -Wall -Werror]) +AM_INIT_AUTOMAKE([1.13 foreign dist-bzip2 dist-xz dist-zip subdir-objects serial-tests -Wall -Wno-extra-portability -Werror]) AC_CONFIG_HEADERS([config.h]) AM_SILENT_RULES([yes]) AM_MAINTAINER_MODE([enable]) AC_USE_SYSTEM_EXTENSIONS -AM_PROG_AR - LT_INIT AC_PROG_CC_C99 -- cgit v1.2.3-18-g5258 From aa8b4d635405db2b8c46e42480789d1c1e14d8c4 Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Fri, 1 Jan 2016 19:57:28 +0100 Subject: build: minor style fixes --- configure.ac | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 43a336c..403bfec 100644 --- a/configure.ac +++ b/configure.ac @@ -1,10 +1,12 @@ AC_PREREQ([2.69]) -AC_INIT([blogc], [0.5.1], [https://github.com/blogc/blogc], [blogc], [https://blogc.rgm.io]) +AC_INIT([blogc], [0.5.1], [https://github.com/blogc/blogc], [blogc], + [https://blogc.rgm.io]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) -AM_INIT_AUTOMAKE([1.13 foreign dist-bzip2 dist-xz dist-zip subdir-objects serial-tests -Wall -Wno-extra-portability -Werror]) +AM_INIT_AUTOMAKE([1.13 foreign dist-bzip2 dist-xz dist-zip subdir-objects + serial-tests -Wall -Wno-extra-portability -Werror]) AC_CONFIG_HEADERS([config.h]) AM_SILENT_RULES([yes]) AM_MAINTAINER_MODE([enable]) -- cgit v1.2.3-18-g5258 From a5b155fde8c69d8954a13a4ff6fbdb7633094da7 Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Mon, 4 Jan 2016 00:33:06 +0100 Subject: renderer: improved foreach tests --- tests/check_renderer.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/check_renderer.c b/tests/check_renderer.c index e380be7..d641506 100644 --- a/tests/check_renderer.c +++ b/tests/check_renderer.c @@ -76,7 +76,8 @@ test_render_entry(void **state) "{% if GUDA < \"zxd\" %}LOL2{% endif %}\n" "{% if GUDA > \"zxd\" %}LOL3{% endif %}\n" "{% if GUDA <= \"zxc\" %}LOL4{% endif %}\n" - "{% foreach TAGS %}lol {{ FOREACH_ITEM }} haha {% endforeach %}\n"; + "{% foreach TAGS %}lol {{ FOREACH_ITEM }} haha {% endforeach %}\n" + "{% foreach TAGS_ASD %}yay{% endforeach %}\n"; blogc_error_t *err = NULL; b_slist_t *l = blogc_template_parse(str, strlen(str), &err); assert_non_null(l); @@ -100,7 +101,8 @@ test_render_entry(void **state) "LOL2\n" "\n" "LOL4\n" - "lol foo haha lol bar haha lol baz haha \n"); + "lol foo haha lol bar haha lol baz haha \n" + "\n"); blogc_template_free_stmts(l); b_slist_free_full(s, (b_free_func_t) b_trie_free); free(out); @@ -121,6 +123,7 @@ test_render_listing(void **state) "{% ifdef DATE_FORMATTED %}{{ DATE_FORMATTED }}{% endif %}\n" "bola: {% ifdef BOLA %}{{ BOLA }}{% endif %}\n" "{% foreach TAGS %}lol {{ FOREACH_ITEM }} haha {% endforeach %}\n" + "{% foreach TAGS_ASD %}yay{% endforeach %}\n" "{% endblock %}\n"; blogc_error_t *err = NULL; b_slist_t *l = blogc_template_parse(str, strlen(str), &err); @@ -138,13 +141,16 @@ test_render_listing(void **state) "bola: asd\n" "lol foo haha lol bar haha lol baz haha \n" "\n" + "\n" "2014-02-03 04:05:06\n" "bola: asd2\n" "\n" "\n" + "\n" "2013-01-02 03:04:05\n" "bola: asd3\n" "\n" + "\n" "\n"); blogc_template_free_stmts(l); b_slist_free_full(s, (b_free_func_t) b_trie_free); -- cgit v1.2.3-18-g5258 From fc0e0f4c2415b816daaca8d292aada532be19faa Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Thu, 7 Jan 2016 03:20:43 +0100 Subject: build: version bump --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 403bfec..ffb61ac 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ AC_PREREQ([2.69]) -AC_INIT([blogc], [0.5.1], [https://github.com/blogc/blogc], [blogc], +AC_INIT([blogc], [0.6], [https://github.com/blogc/blogc], [blogc], [https://blogc.rgm.io]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) -- cgit v1.2.3-18-g5258 From 56a5ebf7f0b0d86b7d6e3fd468d3415da312e69b Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Fri, 8 Jan 2016 02:36:17 +0100 Subject: renderer: handle FOREACH_ITEM as a normal variable --- src/renderer.c | 22 ++++++++++++-------- src/renderer.h | 3 ++- tests/check_renderer.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 67 insertions(+), 14 deletions(-) diff --git a/src/renderer.c b/src/renderer.c index defbe10..2fb60b5 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -58,8 +58,16 @@ blogc_format_date(const char *date, b_trie_t *global, b_trie_t *local) char* -blogc_format_variable(const char *name, b_trie_t *global, b_trie_t *local) +blogc_format_variable(const char *name, b_trie_t *global, b_trie_t *local, + b_slist_t *foreach_var) { + if (0 == strcmp(name, "FOREACH_ITEM")) { + if (foreach_var != NULL && foreach_var->data != NULL) { + return b_strdup(foreach_var->data); + } + return NULL; + } + char *var = NULL; bool must_format = false; if (b_str_ends_with(name, "_FORMATTED")) { @@ -200,13 +208,8 @@ 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) { - if (0 == strcmp(stmt->value, "FOREACH_ITEM")) { // foreach - if (foreach_var != NULL && foreach_var->data != NULL) - b_string_append(str, foreach_var->data); - break; - } config_value = blogc_format_variable(stmt->value, - config, inside_block ? tmp_source : NULL); + config, inside_block ? tmp_source : NULL, foreach_var); if (config_value != NULL) { b_string_append(str, config_value); free(config_value); @@ -237,7 +240,7 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing defined = NULL; if (stmt->value != NULL) defined = blogc_format_variable(stmt->value, config, - inside_block ? tmp_source : NULL); + inside_block ? tmp_source : NULL, foreach_var); evaluate = false; if (stmt->op != 0) { // Strings that start with a '"' are actually strings, the @@ -254,7 +257,8 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing } else { defined2 = blogc_format_variable(stmt->value2, - config, inside_block ? tmp_source : NULL); + config, inside_block ? tmp_source : NULL, + foreach_var); } } diff --git a/src/renderer.h b/src/renderer.h index de26e98..d99fea0 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -14,7 +14,8 @@ const char* blogc_get_variable(const char *name, b_trie_t *global, b_trie_t *local); char* blogc_format_date(const char *date, b_trie_t *global, b_trie_t *local); -char* blogc_format_variable(const char *name, b_trie_t *global, b_trie_t *local); +char* blogc_format_variable(const char *name, b_trie_t *global, b_trie_t *local, + b_slist_t *foreach_var); b_slist_t* blogc_split_list_variable(const char *name, b_trie_t *global, b_trie_t *local); char* blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, diff --git a/tests/check_renderer.c b/tests/check_renderer.c index d641506..014cd84 100644 --- a/tests/check_renderer.c +++ b/tests/check_renderer.c @@ -556,6 +556,31 @@ test_render_foreach(void **state) } +static void +test_render_foreach_if(void **state) +{ + const char *str = + "{% block entry %}\n" + "{% foreach TAGS %} {% if FOREACH_ITEM == \"bar\" %}{{ FOREACH_ITEM }}" + "{% endif %} {% endforeach %}\n" + "{% endblock %}\n"; + blogc_error_t *err = NULL; + b_slist_t *l = blogc_template_parse(str, strlen(str), &err); + assert_non_null(l); + assert_null(err); + b_slist_t *s = create_sources(1); + assert_non_null(s); + char *out = blogc_render(l, s, NULL, false); + assert_string_equal(out, + "\n" + " bar \n" + "\n"); + blogc_template_free_stmts(l); + b_slist_free_full(s, (b_free_func_t) b_trie_free); + free(out); +} + + static void test_render_null(void **state) { @@ -770,13 +795,13 @@ test_format_variable(void **state) b_trie_t *l = b_trie_new(free); b_trie_insert(l, "NAME", b_strdup("chunda")); b_trie_insert(l, "TITLE", b_strdup("chunda2")); - char *tmp = blogc_format_variable("NAME", g, l); + char *tmp = blogc_format_variable("NAME", g, l, NULL); assert_string_equal(tmp, "chunda"); free(tmp); - tmp = blogc_format_variable("TITLE", g, l); + tmp = blogc_format_variable("TITLE", g, l, NULL); assert_string_equal(tmp, "chunda2"); free(tmp); - assert_null(blogc_format_variable("BOLA", g, l)); + assert_null(blogc_format_variable("BOLA", g, l, NULL)); b_trie_free(g); b_trie_free(l); } @@ -790,7 +815,7 @@ test_format_variable_with_date(void **state) b_trie_insert(g, "DATE_FORMAT", b_strdup("%R")); b_trie_t *l = b_trie_new(free); b_trie_insert(l, "DATE", b_strdup("2011-12-13 14:15:16")); - char *tmp = blogc_format_variable("DATE_FORMATTED", g, l); + char *tmp = blogc_format_variable("DATE_FORMATTED", g, l, NULL); assert_string_equal(tmp, "14:15"); free(tmp); b_trie_free(g); @@ -798,6 +823,26 @@ test_format_variable_with_date(void **state) } +static void +test_format_variable_foreach(void **state) +{ + b_slist_t *l = NULL; + l = b_slist_append(l, b_strdup("asd")); + l = b_slist_append(l, b_strdup("qwe")); + char *tmp = blogc_format_variable("FOREACH_ITEM", NULL, NULL, l->next); + assert_string_equal(tmp, "qwe"); + free(tmp); + b_slist_free_full(l, free); +} + + +static void +test_format_variable_foreach_empty(void **state) +{ + assert_null(blogc_format_variable("FOREACH_ITEM", NULL, NULL, NULL)); +} + + static void test_split_list_variable(void **state) { @@ -847,6 +892,7 @@ main(void) unit_test(test_render_if_lt_eq), unit_test(test_render_if_gt_eq), unit_test(test_render_foreach), + unit_test(test_render_foreach_if), unit_test(test_render_null), unit_test(test_render_outside_block), unit_test(test_render_prefer_local_variable), @@ -860,6 +906,8 @@ main(void) unit_test(test_format_date_without_date), unit_test(test_format_variable), unit_test(test_format_variable_with_date), + unit_test(test_format_variable_foreach), + unit_test(test_format_variable_foreach_empty), unit_test(test_split_list_variable), unit_test(test_split_list_variable_not_found), }; -- cgit v1.2.3-18-g5258 From 687fa0ef362dfdacb6ee0a169d7ab0a84e747cc4 Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Fri, 8 Jan 2016 18:53:51 +0100 Subject: fixed copyright --- LICENSE | 2 +- src/content-parser.c | 2 +- src/content-parser.h | 2 +- src/datetime-parser.c | 2 +- src/datetime-parser.h | 2 +- src/error.c | 2 +- src/error.h | 2 +- src/file.c | 2 +- src/file.h | 2 +- src/loader.c | 2 +- src/loader.h | 2 +- src/main.c | 2 +- src/renderer.c | 2 +- src/renderer.h | 2 +- src/source-parser.c | 2 +- src/source-parser.h | 2 +- src/template-parser.c | 2 +- src/template-parser.h | 2 +- src/utils/mem.c | 2 +- src/utils/slist.c | 2 +- src/utils/strings.c | 2 +- src/utils/trie.c | 2 +- src/utils/utils.h | 2 +- tests/check_content_parser.c | 2 +- tests/check_datetime_parser.c | 2 +- tests/check_error.c | 2 +- tests/check_loader.c | 2 +- tests/check_renderer.c | 2 +- tests/check_source_parser.c | 2 +- tests/check_template_parser.c | 2 +- tests/check_utils.c | 2 +- 31 files changed, 31 insertions(+), 31 deletions(-) diff --git a/LICENSE b/LICENSE index 167fbc7..0f0ff43 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2015, Rafael G. Martins +Copyright (c) 2014-2016, Rafael G. Martins All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/src/content-parser.c b/src/content-parser.c index f5450d6..b22eb70 100644 --- a/src/content-parser.c +++ b/src/content-parser.c @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins + * Copyright (C) 2015-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/src/content-parser.h b/src/content-parser.h index 2f6b8b9..5802594 100644 --- a/src/content-parser.h +++ b/src/content-parser.h @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins + * Copyright (C) 2015-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/src/datetime-parser.c b/src/datetime-parser.c index 6a2162d..8785a89 100644 --- a/src/datetime-parser.c +++ b/src/datetime-parser.c @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins + * Copyright (C) 2015-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/src/datetime-parser.h b/src/datetime-parser.h index 7f94545..a5087b3 100644 --- a/src/datetime-parser.h +++ b/src/datetime-parser.h @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins + * Copyright (C) 2015-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/src/error.c b/src/error.c index 28396f8..6256873 100644 --- a/src/error.c +++ b/src/error.c @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins + * Copyright (C) 2015-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/src/error.h b/src/error.h index 845a316..caa86b0 100644 --- a/src/error.h +++ b/src/error.h @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins + * Copyright (C) 2015-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/src/file.c b/src/file.c index d660afc..a4c763a 100644 --- a/src/file.c +++ b/src/file.c @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins + * Copyright (C) 2015-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/src/file.h b/src/file.h index 00145ee..97e5274 100644 --- a/src/file.h +++ b/src/file.h @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins + * Copyright (C) 2015-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/src/loader.c b/src/loader.c index 5cd24df..baa81fa 100644 --- a/src/loader.c +++ b/src/loader.c @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins + * Copyright (C) 2015-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/src/loader.h b/src/loader.h index 610aa42..c432e20 100644 --- a/src/loader.h +++ b/src/loader.h @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins + * Copyright (C) 2015-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/src/main.c b/src/main.c index 600a131..f3f990b 100644 --- a/src/main.c +++ b/src/main.c @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins + * Copyright (C) 2015-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/src/renderer.c b/src/renderer.c index 2fb60b5..0f5d10a 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins + * Copyright (C) 2015-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/src/renderer.h b/src/renderer.h index d99fea0..15204e6 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins + * Copyright (C) 2015-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/src/source-parser.c b/src/source-parser.c index db0792c..65fdd4e 100644 --- a/src/source-parser.c +++ b/src/source-parser.c @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins + * Copyright (C) 2015-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/src/source-parser.h b/src/source-parser.h index d92b1ce..f359f9e 100644 --- a/src/source-parser.h +++ b/src/source-parser.h @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins + * Copyright (C) 2015-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/src/template-parser.c b/src/template-parser.c index 030ecca..0783254 100644 --- a/src/template-parser.c +++ b/src/template-parser.c @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins + * Copyright (C) 2015-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/src/template-parser.h b/src/template-parser.h index 9da5fff..5add574 100644 --- a/src/template-parser.h +++ b/src/template-parser.h @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins + * Copyright (C) 2015-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/src/utils/mem.c b/src/utils/mem.c index 7c5e0a2..693d555 100644 --- a/src/utils/mem.c +++ b/src/utils/mem.c @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2014-2015 Rafael G. Martins + * Copyright (C) 2014-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/src/utils/slist.c b/src/utils/slist.c index 3d9b892..9753aa7 100644 --- a/src/utils/slist.c +++ b/src/utils/slist.c @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2014-2015 Rafael G. Martins + * Copyright (C) 2014-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/src/utils/strings.c b/src/utils/strings.c index 40174a1..6f10d56 100644 --- a/src/utils/strings.c +++ b/src/utils/strings.c @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2014-2015 Rafael G. Martins + * Copyright (C) 2014-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/src/utils/trie.c b/src/utils/trie.c index 72a62f6..b8c1e63 100644 --- a/src/utils/trie.c +++ b/src/utils/trie.c @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2014-2015 Rafael G. Martins + * Copyright (C) 2014-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/src/utils/utils.h b/src/utils/utils.h index 5a1505b..49a7735 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2014-2015 Rafael G. Martins + * Copyright (C) 2014-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/tests/check_content_parser.c b/tests/check_content_parser.c index dc3485e..8ed9520 100644 --- a/tests/check_content_parser.c +++ b/tests/check_content_parser.c @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins + * Copyright (C) 2015-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/tests/check_datetime_parser.c b/tests/check_datetime_parser.c index 1ac976d..ba5a79d 100644 --- a/tests/check_datetime_parser.c +++ b/tests/check_datetime_parser.c @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins + * Copyright (C) 2015-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/tests/check_error.c b/tests/check_error.c index 17e1c40..d3af9c0 100644 --- a/tests/check_error.c +++ b/tests/check_error.c @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins + * Copyright (C) 2015-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/tests/check_loader.c b/tests/check_loader.c index 03f0ef1..ac8bdb3 100644 --- a/tests/check_loader.c +++ b/tests/check_loader.c @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins + * Copyright (C) 2015-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/tests/check_renderer.c b/tests/check_renderer.c index 014cd84..360f067 100644 --- a/tests/check_renderer.c +++ b/tests/check_renderer.c @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins + * Copyright (C) 2015-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/tests/check_source_parser.c b/tests/check_source_parser.c index 4d8518e..8d6c039 100644 --- a/tests/check_source_parser.c +++ b/tests/check_source_parser.c @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins + * Copyright (C) 2015-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/tests/check_template_parser.c b/tests/check_template_parser.c index 145a27e..f9fd71a 100644 --- a/tests/check_template_parser.c +++ b/tests/check_template_parser.c @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2015 Rafael G. Martins + * Copyright (C) 2015-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. diff --git a/tests/check_utils.c b/tests/check_utils.c index a42c75a..cb24625 100644 --- a/tests/check_utils.c +++ b/tests/check_utils.c @@ -1,6 +1,6 @@ /* * blogc: A blog compiler. - * Copyright (C) 2014-2015 Rafael G. Martins + * Copyright (C) 2014-2016 Rafael G. Martins * * This program can be distributed under the terms of the BSD License. * See the file LICENSE. -- cgit v1.2.3-18-g5258 From 170210579bbe1259315eab2c7f5893448b3f961c Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Sat, 9 Jan 2016 23:47:18 +0100 Subject: build: add build-aux/build-windows.sh to tarball --- Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile.am b/Makefile.am index 78738cc..e143b0b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,6 +12,7 @@ AM_DISTCHECK_CONFIGURE_FLAGS = \ ## File listings EXTRA_DIST = \ + build-aux/build-windows.sh \ autogen.sh \ LICENSE \ README.md \ -- cgit v1.2.3-18-g5258 From 52ee02d97169ee59072c24e2ac5d717cfaf359fc Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Sun, 10 Jan 2016 01:06:32 +0100 Subject: build: improved windows build script --- build-aux/build-windows.sh | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/build-aux/build-windows.sh b/build-aux/build-windows.sh index 267a8aa..bdecc8b 100755 --- a/build-aux/build-windows.sh +++ b/build-aux/build-windows.sh @@ -5,24 +5,18 @@ # # mingw32-gcc mingw64-gcc zip # -# This script must be called with the xz source tarball as argument. +# This script must be called with the xz source tarball and the target version +# as arguments. set -ex -[[ $# -eq 1 ]] - - -get_version() { - local a=$(basename "${1}") - a="${a%.tar.xz}" - echo "${a#blogc-}" -} +[[ $# -eq 2 ]] build() { - local version=$(get_version "${1}") - local arch=${2} + local version=${2} + local arch=${3} local build_dir="/tmp/blogc_build_${version}_${arch}" local dest_dir="/tmp/blogc-${version}-w${arch}" @@ -50,5 +44,5 @@ build() { for arch in 32 64; do - build "$1" "${arch}" + build "$1" "$2" "${arch}" done -- cgit v1.2.3-18-g5258 From 6e44d82d3b3ff6f5376fdafa6ee7fa09fc4f8ffb Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Sun, 10 Jan 2016 02:13:25 +0100 Subject: build: added rpm spec --- .gitignore | 1 + blogc.spec.in | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 1 + 3 files changed, 65 insertions(+) create mode 100644 blogc.spec.in diff --git a/.gitignore b/.gitignore index 65e51f1..2ccfd38 100644 --- a/.gitignore +++ b/.gitignore @@ -60,4 +60,5 @@ blogc-*.tar.* blogc-*.zip # rpms +blogc.spec blogc-*.rpm diff --git a/blogc.spec.in b/blogc.spec.in new file mode 100644 index 0000000..96df668 --- /dev/null +++ b/blogc.spec.in @@ -0,0 +1,63 @@ +Name: blogc +Version: @PACKAGE_VERSION@ +Release: 1%{?dist} +License: BSD +Group: Applications/Text +Summary: A blog compiler +URL: http://blogc.org/ +Source0: https://github.com/blogc/blogc/releases/download/v%{version}/blogc-%{version}.tar.xz + +#BuildRequires: +#Requires: + +%description +blogc(1) is a blog compiler. It converts source files and templates into +blog/website resources. + + +%prep +%setup -q -n blogc-%{version} + + +%build +%configure +make %{?_smp_mflags} + + +%install +rm -rf $RPM_BUILD_ROOT +%make_install + + +%files +%{_mandir}/man*/blogc* +%{_bindir}/blogc + +%doc README.md +%license LICENSE + + +%changelog +* Thu Jan 07 2016 Rafael G. Martins 0.6-1 +- New release. + +* Thu Dec 03 2015 Rafael G. Martins 0.5.1-1 +- New release. + +* Thu Nov 05 2015 Rafael G. Martins 0.5-1 +- New release. + +* Sun Oct 25 2015 Rafael G. Martins 0.4-1 +- New release. + +* Fri Oct 16 2015 Rafael G. Martins 0.3-1 +- New release. + +* Thu Oct 08 2015 Rafael G. Martins 0.2.1-1 +- New release. + +* Wed Sep 16 2015 Rafael G. Martins 0.1-1 +- First stable release. + +* Mon Sep 14 2015 Rafael G. Martins 0.1-0.1.beta4 +- Initial package. diff --git a/configure.ac b/configure.ac index ffb61ac..5f6859f 100644 --- a/configure.ac +++ b/configure.ac @@ -101,6 +101,7 @@ LT_LIB_M AC_CONFIG_FILES([ Makefile + blogc.spec ]) AC_OUTPUT -- cgit v1.2.3-18-g5258 From 1c819d729177f6569febb962955dd8098ef6b35b Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Sun, 10 Jan 2016 03:01:11 +0100 Subject: build: version bump --- blogc.spec.in | 3 +++ configure.ac | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/blogc.spec.in b/blogc.spec.in index 96df668..f10e7fc 100644 --- a/blogc.spec.in +++ b/blogc.spec.in @@ -38,6 +38,9 @@ rm -rf $RPM_BUILD_ROOT %changelog +* Sun Jan 10 2016 Rafael G. Martins 0.6.1-1 +- New release. + * Thu Jan 07 2016 Rafael G. Martins 0.6-1 - New release. diff --git a/configure.ac b/configure.ac index 5f6859f..e43633d 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ AC_PREREQ([2.69]) -AC_INIT([blogc], [0.6], [https://github.com/blogc/blogc], [blogc], +AC_INIT([blogc], [0.6.1], [https://github.com/blogc/blogc], [blogc], [https://blogc.rgm.io]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) -- cgit v1.2.3-18-g5258 From 5e53ba7406e65b51ec59aad634b6baf7154e1ad3 Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Wed, 13 Jan 2016 19:31:32 +0100 Subject: build: added git-version-gen support --- .gitignore | 4 + Makefile.am | 15 ++++ build-aux/git-version-gen | 225 ++++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 4 +- 4 files changed, 246 insertions(+), 2 deletions(-) create mode 100755 build-aux/git-version-gen diff --git a/.gitignore b/.gitignore index 2ccfd38..01d2647 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ Makefile.in .dirstamp /build-aux/* !/build-aux/build-windows.sh +!/build-aux/git-version-gen # installed .m4 files /m4/*.m4 @@ -62,3 +63,6 @@ blogc-*.zip # rpms blogc.spec blogc-*.rpm + +# git-version-gen +/.version diff --git a/Makefile.am b/Makefile.am index e143b0b..7b52fa0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -13,6 +13,8 @@ AM_DISTCHECK_CONFIGURE_FLAGS = \ EXTRA_DIST = \ build-aux/build-windows.sh \ + build-aux/git-version-gen \ + $(top_srcdir)/.version \ autogen.sh \ LICENSE \ README.md \ @@ -24,6 +26,10 @@ CLEANFILES = \ MAINTAINERCLEANFILES = \ $(NULL) +BUILT_SOURCES = \ + $(top_srcdir)/.version \ + $(NULL) + noinst_HEADERS = \ src/content-parser.h \ src/datetime-parser.h \ @@ -308,6 +314,15 @@ TESTS = \ $(check_PROGRAMS) +## Helpers: git-version-gen + +$(top_srcdir)/.version: + echo $(VERSION) > $@-t && mv $@-t $@ + +dist-hook: + echo $(VERSION) > $(distdir)/.tarball-version + + ## Helpers: Valgrind runner if USE_VALGRIND diff --git a/build-aux/git-version-gen b/build-aux/git-version-gen new file mode 100755 index 0000000..3468247 --- /dev/null +++ b/build-aux/git-version-gen @@ -0,0 +1,225 @@ +#!/bin/sh +# Print a version string. +scriptversion=2012-12-31.23; # UTC + +# Copyright (C) 2007-2013 Free Software Foundation, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/. +# It may be run two ways: +# - from a git repository in which the "git describe" command below +# produces useful output (thus requiring at least one signed tag) +# - from a non-git-repo directory containing a .tarball-version file, which +# presumes this script is invoked like "./git-version-gen .tarball-version". + +# In order to use intra-version strings in your project, you will need two +# separate generated version string files: +# +# .tarball-version - present only in a distribution tarball, and not in +# a checked-out repository. Created with contents that were learned at +# the last time autoconf was run, and used by git-version-gen. Must not +# be present in either $(srcdir) or $(builddir) for git-version-gen to +# give accurate answers during normal development with a checked out tree, +# but must be present in a tarball when there is no version control system. +# Therefore, it cannot be used in any dependencies. GNUmakefile has +# hooks to force a reconfigure at distribution time to get the value +# correct, without penalizing normal development with extra reconfigures. +# +# .version - present in a checked-out repository and in a distribution +# tarball. Usable in dependencies, particularly for files that don't +# want to depend on config.h but do want to track version changes. +# Delete this file prior to any autoconf run where you want to rebuild +# files to pick up a version string change; and leave it stale to +# minimize rebuild time after unrelated changes to configure sources. +# +# As with any generated file in a VC'd directory, you should add +# /.version to .gitignore, so that you don't accidentally commit it. +# .tarball-version is never generated in a VC'd directory, so needn't +# be listed there. +# +# Use the following line in your configure.ac, so that $(VERSION) will +# automatically be up-to-date each time configure is run (and note that +# since configure.ac no longer includes a version string, Makefile rules +# should not depend on configure.ac for version updates). +# +# AC_INIT([GNU project], +# m4_esyscmd([build-aux/git-version-gen .tarball-version]), +# [bug-project@example]) +# +# Then use the following lines in your Makefile.am, so that .version +# will be present for dependencies, and so that .version and +# .tarball-version will exist in distribution tarballs. +# +# EXTRA_DIST = $(top_srcdir)/.version +# BUILT_SOURCES = $(top_srcdir)/.version +# $(top_srcdir)/.version: +# echo $(VERSION) > $@-t && mv $@-t $@ +# dist-hook: +# echo $(VERSION) > $(distdir)/.tarball-version + + +me=$0 + +version="git-version-gen $scriptversion + +Copyright 2011 Free Software Foundation, Inc. +There is NO warranty. You may redistribute this software +under the terms of the GNU General Public License. +For more information about these matters, see the files named COPYING." + +usage="\ +Usage: $me [OPTION]... \$srcdir/.tarball-version [TAG-NORMALIZATION-SED-SCRIPT] +Print a version string. + +Options: + + --prefix prefix of git tags (default 'v') + --fallback fallback version to use if \"git --version\" fails + + --help display this help and exit + --version output version information and exit + +Running without arguments will suffice in most cases." + +prefix=v +fallback= + +while test $# -gt 0; do + case $1 in + --help) echo "$usage"; exit 0;; + --version) echo "$version"; exit 0;; + --prefix) shift; prefix="$1";; + --fallback) shift; fallback="$1";; + -*) + echo "$0: Unknown option '$1'." >&2 + echo "$0: Try '--help' for more information." >&2 + exit 1;; + *) + if test "x$tarball_version_file" = x; then + tarball_version_file="$1" + elif test "x$tag_sed_script" = x; then + tag_sed_script="$1" + else + echo "$0: extra non-option argument '$1'." >&2 + exit 1 + fi;; + esac + shift +done + +if test "x$tarball_version_file" = x; then + echo "$usage" + exit 1 +fi + +tag_sed_script="${tag_sed_script:-s/x/x/}" + +nl=' +' + +# Avoid meddling by environment variable of the same name. +v= +v_from_git= + +# First see if there is a tarball-only version file. +# then try "git describe", then default. +if test -f $tarball_version_file +then + v=`cat $tarball_version_file` || v= + case $v in + *$nl*) v= ;; # reject multi-line output + [0-9]*) ;; + *) v= ;; + esac + test "x$v" = x \ + && echo "$0: WARNING: $tarball_version_file is missing or damaged" 1>&2 +fi + +if test "x$v" != x +then + : # use $v +# Otherwise, if there is at least one git commit involving the working +# directory, and "git describe" output looks sensible, use that to +# derive a version string. +elif test "`git log -1 --pretty=format:x . 2>&1`" = x \ + && v=`git describe --abbrev=4 --match="$prefix*" HEAD 2>/dev/null \ + || git describe --abbrev=4 HEAD 2>/dev/null` \ + && v=`printf '%s\n' "$v" | sed "$tag_sed_script"` \ + && case $v in + $prefix[0-9]*) ;; + *) (exit 1) ;; + esac +then + # Is this a new git that lists number of commits since the last + # tag or the previous older version that did not? + # Newer: v6.10-77-g0f8faeb + # Older: v6.10-g0f8faeb + case $v in + *-*-*) : git describe is okay three part flavor ;; + *-*) + : git describe is older two part flavor + # Recreate the number of commits and rewrite such that the + # result is the same as if we were using the newer version + # of git describe. + vtag=`echo "$v" | sed 's/-.*//'` + commit_list=`git rev-list "$vtag"..HEAD 2>/dev/null` \ + || { commit_list=failed; + echo "$0: WARNING: git rev-list failed" 1>&2; } + numcommits=`echo "$commit_list" | wc -l` + v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`; + test "$commit_list" = failed && v=UNKNOWN + ;; + esac + + # Change the first '-' to a '.', so version-comparing tools work properly. + # Remove the "g" in git describe's output string, to save a byte. + v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`; + v_from_git=1 +elif test "x$fallback" = x || git --version >/dev/null 2>&1; then + v=UNKNOWN +else + v=$fallback +fi + +v=`echo "$v" |sed "s/^$prefix//"` + +# Test whether to append the "-dirty" suffix only if the version +# string we're using came from git. I.e., skip the test if it's "UNKNOWN" +# or if it came from .tarball-version. +if test "x$v_from_git" != x; then + # Don't declare a version "dirty" merely because a time stamp has changed. + git update-index --refresh > /dev/null 2>&1 + + dirty=`exec 2>/dev/null;git diff-index --name-only HEAD` || dirty= + case "$dirty" in + '') ;; + *) # Append the suffix only if there isn't one already. + case $v in + *-dirty) ;; + *) v="$v-dirty" ;; + esac ;; + esac +fi + +# Omit the trailing newline, so that m4_esyscmd can use the result directly. +echo "$v" | tr -d "$nl" + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/configure.ac b/configure.ac index e43633d..f837f1b 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ AC_PREREQ([2.69]) -AC_INIT([blogc], [0.6.1], [https://github.com/blogc/blogc], [blogc], - [https://blogc.rgm.io]) +AC_INIT([blogc], m4_esyscmd([build-aux/git-version-gen .tarball-version]), + [https://github.com/blogc/blogc], [blogc], [https://blogc.rgm.io]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) -- cgit v1.2.3-18-g5258 From f7aa4a3269a21f4d0c83f11a0aef4ccf821ce6e2 Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Thu, 14 Jan 2016 03:50:42 +0100 Subject: template-parser: added whitespace cleaners. needs more tests and docs --- src/template-parser.c | 99 +++++++++++++++++++++++++++++++++++++------ src/template-parser.h | 5 +++ src/utils/strings.c | 45 ++++++++++++++++---- src/utils/utils.h | 2 + tests/check_template_parser.c | 48 +++++++++++++++------ tests/check_utils.c | 55 ++++++++++++++++++++++++ 6 files changed, 218 insertions(+), 36 deletions(-) diff --git a/src/template-parser.c b/src/template-parser.c index 0783254..1d9046e 100644 --- a/src/template-parser.c +++ b/src/template-parser.c @@ -22,6 +22,7 @@ typedef enum { TEMPLATE_START = 1, TEMPLATE_OPEN_BRACKET, TEMPLATE_BLOCK_START, + TEMPLATE_BLOCK_START_WHITESPACE_CLEANER, TEMPLATE_BLOCK_TYPE, TEMPLATE_BLOCK_BLOCK_TYPE_START, TEMPLATE_BLOCK_BLOCK_TYPE, @@ -34,6 +35,7 @@ typedef enum { TEMPLATE_BLOCK_IF_VARIABLE_OPERAND, TEMPLATE_BLOCK_FOREACH_START, TEMPLATE_BLOCK_FOREACH_VARIABLE, + TEMPLATE_BLOCK_END_WHITESPACE_CLEANER, TEMPLATE_BLOCK_END, TEMPLATE_VARIABLE_START, TEMPLATE_VARIABLE, @@ -72,6 +74,20 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) b_slist_t *stmts = NULL; blogc_template_stmt_t *stmt = NULL; + /* + * this is a reference to the content of previous node in the singly-linked + * list. The "correct" solution here would be implement a doubly-linked + * list, but here are a few reasons to avoid it: + * + * - i'm too tired to implement it :P + * - template parser never walk backwards, then the list itself does not + * need to know its previous node. + */ + blogc_template_stmt_t *previous = NULL; + + bool lstrip_next = false; + char *tmp = NULL; + blogc_template_parser_state_t state = TEMPLATE_START; blogc_template_parser_block_state_t block_state = BLOCK_CLOSED; blogc_template_stmt_type_t type = BLOGC_TEMPLATE_CONTENT_STMT; @@ -86,10 +102,20 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) if (last) { stmt = b_malloc(sizeof(blogc_template_stmt_t)); stmt->type = type; - stmt->value = b_strndup(src + start, src_len - start); + if (lstrip_next) { + tmp = b_strndup(src + start, src_len - start); + stmt->value = b_strdup(b_str_lstrip(tmp)); + free(tmp); + tmp = NULL; + lstrip_next = false; + } + else { + stmt->value = b_strndup(src + start, src_len - start); + } stmt->op = 0; stmt->value2 = NULL; stmts = b_slist_append(stmts, stmt); + previous = stmt; stmt = NULL; } if (c == '{') { @@ -101,16 +127,26 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) case TEMPLATE_OPEN_BRACKET: if (c == '%' || c == '{') { if (c == '%') - state = TEMPLATE_BLOCK_START; + state = TEMPLATE_BLOCK_START_WHITESPACE_CLEANER; else state = TEMPLATE_VARIABLE_START; if (end > start) { stmt = b_malloc(sizeof(blogc_template_stmt_t)); stmt->type = type; - stmt->value = b_strndup(src + start, end - start); + if (lstrip_next) { + tmp = b_strndup(src + start, end - start); + stmt->value = b_strdup(b_str_lstrip(tmp)); + free(tmp); + tmp = NULL; + lstrip_next = false; + } + else { + stmt->value = b_strndup(src + start, end - start); + } stmt->op = 0; stmt->value2 = NULL; stmts = b_slist_append(stmts, stmt); + previous = stmt; stmt = NULL; } break; @@ -118,6 +154,18 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) state = TEMPLATE_START; break; + case TEMPLATE_BLOCK_START_WHITESPACE_CLEANER: + if (c == '-') { + if ((previous != NULL) && + (previous->type == BLOGC_TEMPLATE_CONTENT_STMT)) + { + previous->value = b_str_rstrip(previous->value); // does not need copy + } + state = TEMPLATE_BLOCK_START; + break; + } + state = TEMPLATE_BLOCK_START; + case TEMPLATE_BLOCK_START: if (c == ' ') break; @@ -126,6 +174,13 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) start = current; break; } + if (c == '-') { + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, + src_len, current, + "Invalid statement syntax. Duplicated whitespace " + "cleaner before statement."); + break; + } *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, src_len, current, "Invalid statement syntax. Must begin with lowercase letter."); @@ -152,7 +207,7 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) (0 == strncmp("endblock", src + start, 8))) { if (block_state != BLOCK_CLOSED) { - state = TEMPLATE_BLOCK_END; + state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER; type = BLOGC_TEMPLATE_ENDBLOCK_STMT; block_state = BLOCK_CLOSED; break; @@ -193,7 +248,7 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) (0 == strncmp("endif", src + start, 5))) { if (if_count > 0) { - state = TEMPLATE_BLOCK_END; + state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER; type = BLOGC_TEMPLATE_ENDIF_STMT; if_count--; break; @@ -223,7 +278,7 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) (0 == strncmp("endforeach", src + start, 10))) { if (foreach_open) { - state = TEMPLATE_BLOCK_END; + state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER; type = BLOGC_TEMPLATE_ENDFOREACH_STMT; foreach_open = false; break; @@ -264,7 +319,7 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) { block_state = BLOCK_ENTRY; end = current; - state = TEMPLATE_BLOCK_END; + state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER; break; } else if ((current - start == 7) && @@ -272,7 +327,7 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) { block_state = BLOCK_LISTING; end = current; - state = TEMPLATE_BLOCK_END; + state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER; break; } else if ((current - start == 12) && @@ -280,7 +335,7 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) { block_state = BLOCK_LISTING_ONCE; end = current; - state = TEMPLATE_BLOCK_END; + state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER; break; } } @@ -311,7 +366,7 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) if (type == BLOGC_TEMPLATE_IF_STMT) state = TEMPLATE_BLOCK_IF_OPERATOR_START; else - state = TEMPLATE_BLOCK_END; + state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER; break; } *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, @@ -361,14 +416,14 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) break; if (c == '"' && src[current - 1] == '\\') break; - state = TEMPLATE_BLOCK_END; + state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER; end2 = current + 1; break; case TEMPLATE_BLOCK_IF_VARIABLE_OPERAND: if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_') break; - state = TEMPLATE_BLOCK_END; + state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER; end2 = current; break; @@ -391,7 +446,7 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) break; if (c == ' ') { end = current; - state = TEMPLATE_BLOCK_END; + state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER; break; } *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, @@ -400,13 +455,28 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) "number or '_'."); break; - case TEMPLATE_BLOCK_END: + case TEMPLATE_BLOCK_END_WHITESPACE_CLEANER: if (c == ' ') break; + if (c == '-') { + lstrip_next = true; + state = TEMPLATE_BLOCK_END; + break; + } + state = TEMPLATE_BLOCK_END; + + case TEMPLATE_BLOCK_END: if (c == '%') { state = TEMPLATE_CLOSE_BRACKET; break; } + if (c == '-') { + *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, + src_len, current, + "Invalid statement syntax. Duplicated whitespace " + "cleaner after statement."); + break; + } *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, src_len, current, "Invalid statement syntax. Must end with '%%}'."); @@ -502,6 +572,7 @@ blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err) end2 = 0; } stmts = b_slist_append(stmts, stmt); + previous = stmt; stmt = NULL; state = TEMPLATE_START; type = BLOGC_TEMPLATE_CONTENT_STMT; diff --git a/src/template-parser.h b/src/template-parser.h index 5add574..6cd2c80 100644 --- a/src/template-parser.h +++ b/src/template-parser.h @@ -12,6 +12,11 @@ #include "utils/utils.h" #include "error.h" +/* + * note: whitespace cleaners are NOT added to ast. we fix strings right during + * template parsing. renderer does not need to care about it, for the sake of + * simplicity. + */ typedef enum { BLOGC_TEMPLATE_IFDEF_STMT = 1, BLOGC_TEMPLATE_IFNDEF_STMT, diff --git a/src/utils/strings.c b/src/utils/strings.c index 6f10d56..846ae95 100644 --- a/src/utils/strings.c +++ b/src/utils/strings.c @@ -103,22 +103,44 @@ b_str_ends_with(const char *str, const char *suffix) char* -b_str_strip(char *str) +b_str_lstrip(char *str) { if (str == NULL) - return str; + return NULL; + int i; + size_t str_len = strlen(str); + for (i = 0; i < str_len; i++) { + if ((str[i] != ' ') && (str[i] != '\t') && (str[i] != '\n') && + (str[i] != '\r') && (str[i] != '\t')) + { + str += i; + break; + } + if (i == str_len - 1) { + str += str_len; + break; + } + } + return str; +} + + +char* +b_str_rstrip(char *str) +{ + if (str == NULL) + return NULL; int i; size_t str_len = strlen(str); for (i = str_len - 1; i >= 0; i--) { - if (!isspace(str[i])) { + if ((str[i] != ' ') && (str[i] != '\t') && (str[i] != '\n') && + (str[i] != '\r') && (str[i] != '\t')) + { str[i + 1] = '\0'; break; } - } - str_len = strlen(str); - for (i = 0; i < str_len; i++) { - if (!isspace(str[i])) { - str = str + i; + if (i == 0) { + str[0] = '\0'; break; } } @@ -126,6 +148,13 @@ b_str_strip(char *str) } +char* +b_str_strip(char *str) +{ + return b_str_lstrip(b_str_rstrip(str)); +} + + char** b_str_split(const char *str, char c, unsigned int max_pieces) { diff --git a/src/utils/utils.h b/src/utils/utils.h index 49a7735..dc67497 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -50,6 +50,8 @@ char* b_strdup_vprintf(const char *format, va_list ap); char* b_strdup_printf(const char *format, ...); bool b_str_starts_with(const char *str, const char *prefix); bool b_str_ends_with(const char *str, const char *suffix); +char* b_str_lstrip(char *str); +char* b_str_rstrip(char *str); char* b_str_strip(char *str); char** b_str_split(const char *str, char c, unsigned int max_pieces); char* b_str_replace(const char *str, const char search, const char *replace); diff --git a/tests/check_template_parser.c b/tests/check_template_parser.c index f9fd71a..f655896 100644 --- a/tests/check_template_parser.c +++ b/tests/check_template_parser.c @@ -51,27 +51,27 @@ test_template_parse(void **state) const char *a = "Test\n" "\n" - " {% block entry %}\n" + " {%- block entry -%}\n" "{% ifdef CHUNDA %}\n" "bola\n" "{% endif %}\n" "{% ifndef BOLA %}\n" "bolao\n" - "{% endif %}\n" + "{%- endif %}\n" "{% endblock %}\n" "{% block listing %}{{ BOLA }}{% endblock %}\n" "{% block listing_once %}asd{% endblock %}\n" - "{% foreach BOLA %}hahaha{% endforeach %}\n" + "{%- foreach BOLA %}hahaha{% endforeach %}\n" "{% if BOLA == \"1\\\"0\" %}aee{% endif %}"; blogc_error_t *err = NULL; b_slist_t *stmts = blogc_template_parse(a, strlen(a), &err); assert_null(err); assert_non_null(stmts); - blogc_assert_template_stmt(stmts, "Test\n\n ", + blogc_assert_template_stmt(stmts, "Test", BLOGC_TEMPLATE_CONTENT_STMT); blogc_assert_template_stmt(stmts->next, "entry", BLOGC_TEMPLATE_BLOCK_STMT); - blogc_assert_template_stmt(stmts->next->next, "\n", + blogc_assert_template_stmt(stmts->next->next, "", BLOGC_TEMPLATE_CONTENT_STMT); blogc_assert_template_stmt(stmts->next->next->next, "CHUNDA", BLOGC_TEMPLATE_IFDEF_STMT); @@ -83,7 +83,7 @@ test_template_parse(void **state) BLOGC_TEMPLATE_CONTENT_STMT); b_slist_t *tmp = stmts->next->next->next->next->next->next->next; blogc_assert_template_stmt(tmp, "BOLA", BLOGC_TEMPLATE_IFNDEF_STMT); - blogc_assert_template_stmt(tmp->next, "\nbolao\n", BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(tmp->next, "\nbolao", BLOGC_TEMPLATE_CONTENT_STMT); blogc_assert_template_stmt(tmp->next->next, NULL, BLOGC_TEMPLATE_ENDIF_STMT); blogc_assert_template_stmt(tmp->next->next->next, "\n", BLOGC_TEMPLATE_CONTENT_STMT); @@ -105,7 +105,7 @@ test_template_parse(void **state) blogc_assert_template_stmt(tmp->next->next->next->next->next->next->next->next, NULL, BLOGC_TEMPLATE_ENDBLOCK_STMT); blogc_assert_template_stmt(tmp->next->next->next->next->next->next->next->next->next, - "\n", BLOGC_TEMPLATE_CONTENT_STMT); + "", BLOGC_TEMPLATE_CONTENT_STMT); tmp = tmp->next->next->next->next->next->next->next->next->next->next; blogc_assert_template_stmt(tmp, "BOLA", BLOGC_TEMPLATE_FOREACH_STMT); blogc_assert_template_stmt(tmp->next, "hahaha", @@ -131,27 +131,27 @@ test_template_parse_crlf(void **state) const char *a = "Test\r\n" "\r\n" - " {% block entry %}\r\n" + " {%- block entry -%}\r\n" "{% ifdef CHUNDA %}\r\n" "bola\r\n" "{% endif %}\r\n" "{% ifndef BOLA %}\r\n" "bolao\r\n" - "{% endif %}\r\n" + "{%- endif %}\r\n" "{% endblock %}\r\n" "{% block listing %}{{ BOLA }}{% endblock %}\r\n" "{% block listing_once %}asd{% endblock %}\r\n" - "{% foreach BOLA %}hahaha{% endforeach %}\r\n" + "{%- foreach BOLA %}hahaha{% endforeach %}\r\n" "{% if BOLA == \"1\\\"0\" %}aee{% endif %}"; blogc_error_t *err = NULL; b_slist_t *stmts = blogc_template_parse(a, strlen(a), &err); assert_null(err); assert_non_null(stmts); - blogc_assert_template_stmt(stmts, "Test\r\n\r\n ", + blogc_assert_template_stmt(stmts, "Test", BLOGC_TEMPLATE_CONTENT_STMT); blogc_assert_template_stmt(stmts->next, "entry", BLOGC_TEMPLATE_BLOCK_STMT); - blogc_assert_template_stmt(stmts->next->next, "\r\n", + blogc_assert_template_stmt(stmts->next->next, "", BLOGC_TEMPLATE_CONTENT_STMT); blogc_assert_template_stmt(stmts->next->next->next, "CHUNDA", BLOGC_TEMPLATE_IFDEF_STMT); @@ -163,7 +163,7 @@ test_template_parse_crlf(void **state) BLOGC_TEMPLATE_CONTENT_STMT); b_slist_t *tmp = stmts->next->next->next->next->next->next->next; blogc_assert_template_stmt(tmp, "BOLA", BLOGC_TEMPLATE_IFNDEF_STMT); - blogc_assert_template_stmt(tmp->next, "\r\nbolao\r\n", BLOGC_TEMPLATE_CONTENT_STMT); + blogc_assert_template_stmt(tmp->next, "\r\nbolao", BLOGC_TEMPLATE_CONTENT_STMT); blogc_assert_template_stmt(tmp->next->next, NULL, BLOGC_TEMPLATE_ENDIF_STMT); blogc_assert_template_stmt(tmp->next->next->next, "\r\n", BLOGC_TEMPLATE_CONTENT_STMT); @@ -185,7 +185,7 @@ test_template_parse_crlf(void **state) blogc_assert_template_stmt(tmp->next->next->next->next->next->next->next->next, NULL, BLOGC_TEMPLATE_ENDBLOCK_STMT); blogc_assert_template_stmt(tmp->next->next->next->next->next->next->next->next->next, - "\r\n", BLOGC_TEMPLATE_CONTENT_STMT); + "", BLOGC_TEMPLATE_CONTENT_STMT); tmp = tmp->next->next->next->next->next->next->next->next->next->next; blogc_assert_template_stmt(tmp, "BOLA", BLOGC_TEMPLATE_FOREACH_STMT); blogc_assert_template_stmt(tmp->next, "hahaha", @@ -381,6 +381,26 @@ test_template_parse_invalid_block_start(void **state) "Invalid statement syntax. Must begin with lowercase letter.\n" "Error occurred near line 1, position 4: {% ASD %}"); blogc_error_free(err); + a = "{%-- block entry %}\n"; + err = NULL; + stmts = blogc_template_parse(a, strlen(a), &err); + assert_non_null(err); + assert_null(stmts); + assert_int_equal(err->type, BLOGC_ERROR_TEMPLATE_PARSER); + assert_string_equal(err->msg, + "Invalid statement syntax. Duplicated whitespace cleaner before statement.\n" + "Error occurred near line 1, position 4: {%-- block entry %}"); + blogc_error_free(err); + a = "{% block entry --%}\n"; + err = NULL; + stmts = blogc_template_parse(a, strlen(a), &err); + assert_non_null(err); + assert_null(stmts); + assert_int_equal(err->type, BLOGC_ERROR_TEMPLATE_PARSER); + assert_string_equal(err->msg, + "Invalid statement syntax. Duplicated whitespace cleaner after statement.\n" + "Error occurred near line 1, position 17: {% block entry --%}"); + blogc_error_free(err); } diff --git a/tests/check_utils.c b/tests/check_utils.c index cb24625..a511dda 100644 --- a/tests/check_utils.c +++ b/tests/check_utils.c @@ -128,6 +128,50 @@ test_str_ends_with(void **state) } +static void +test_str_lstrip(void **state) +{ + char *str = b_strdup(" \tbola\n \t"); + assert_string_equal(b_str_lstrip(str), "bola\n \t"); + free(str); + str = b_strdup("guda"); + assert_string_equal(b_str_lstrip(str), "guda"); + free(str); + str = b_strdup("\n"); + assert_string_equal(b_str_lstrip(str), ""); + free(str); + str = b_strdup("\t \n"); + assert_string_equal(b_str_lstrip(str), ""); + free(str); + str = b_strdup(""); + assert_string_equal(b_str_lstrip(str), ""); + free(str); + assert_null(b_str_lstrip(NULL)); +} + + +static void +test_str_rstrip(void **state) +{ + char *str = b_strdup(" \tbola\n \t"); + assert_string_equal(b_str_rstrip(str), " \tbola"); + free(str); + str = b_strdup("guda"); + assert_string_equal(b_str_rstrip(str), "guda"); + free(str); + str = b_strdup("\n"); + assert_string_equal(b_str_rstrip(str), ""); + free(str); + str = b_strdup("\t \n"); + assert_string_equal(b_str_rstrip(str), ""); + free(str); + str = b_strdup(""); + assert_string_equal(b_str_rstrip(str), ""); + free(str); + assert_null(b_str_rstrip(NULL)); +} + + static void test_str_strip(void **state) { @@ -137,6 +181,15 @@ test_str_strip(void **state) str = b_strdup("guda"); assert_string_equal(b_str_strip(str), "guda"); free(str); + str = b_strdup("\n"); + assert_string_equal(b_str_strip(str), ""); + free(str); + str = b_strdup("\t \n"); + assert_string_equal(b_str_strip(str), ""); + free(str); + str = b_strdup(""); + assert_string_equal(b_str_strip(str), ""); + free(str); assert_null(b_str_strip(NULL)); } @@ -799,6 +852,8 @@ main(void) unit_test(test_strdup_printf), unit_test(test_str_starts_with), unit_test(test_str_ends_with), + unit_test(test_str_lstrip), + unit_test(test_str_rstrip), unit_test(test_str_strip), unit_test(test_str_split), unit_test(test_str_replace), -- cgit v1.2.3-18-g5258 From a8cde98ad6b747142ea1798f00a6b8c11b208709 Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Thu, 14 Jan 2016 21:42:52 +0100 Subject: utils: string: strip form-feed and vertical-tab chars as whitespaces --- src/utils/strings.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/utils/strings.c b/src/utils/strings.c index 846ae95..3151612 100644 --- a/src/utils/strings.c +++ b/src/utils/strings.c @@ -111,7 +111,8 @@ b_str_lstrip(char *str) size_t str_len = strlen(str); for (i = 0; i < str_len; i++) { if ((str[i] != ' ') && (str[i] != '\t') && (str[i] != '\n') && - (str[i] != '\r') && (str[i] != '\t')) + (str[i] != '\r') && (str[i] != '\t') && (str[i] != '\f') && + (str[i] != '\v')) { str += i; break; @@ -134,7 +135,8 @@ b_str_rstrip(char *str) size_t str_len = strlen(str); for (i = str_len - 1; i >= 0; i--) { if ((str[i] != ' ') && (str[i] != '\t') && (str[i] != '\n') && - (str[i] != '\r') && (str[i] != '\t')) + (str[i] != '\r') && (str[i] != '\t') && (str[i] != '\f') && + (str[i] != '\v')) { str[i + 1] = '\0'; break; -- cgit v1.2.3-18-g5258 From 542b1d30cca5d6c17b662438d594a91b864ccc56 Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Thu, 14 Jan 2016 21:43:39 +0100 Subject: man: added documentation about whitespace control --- man/blogc-template.7.ronn | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/man/blogc-template.7.ronn b/man/blogc-template.7.ronn index 3a665ab..bd38618 100644 --- a/man/blogc-template.7.ronn +++ b/man/blogc-template.7.ronn @@ -199,6 +199,19 @@ iteration. If the value of the `TAGS` variable is "item1 item2 item3", this template is rendered 3 times, one for each item value. +## WHITESPACE CONTROL + +Users can control how whitespaces (space, form-feed (`\f`), newline (`\n`), +carriage return (`\r`), horizontal tab (`\t`), and vertical tab (`\v`)) are +handled before and after statements delimited with `{%` and `%}` sequences, +respectively. + +Adding a minus sign (`-`) after a `{%` sequence (`{%-`) will remove whitespaces +before the sequence and after the last non-whitespace character before the sequence. + +Adding a minus sign (`-`) before a `%}` sequence (`-%}`) will remove whitespaces +after the sequence and before the first non-whitespace character after the sequence. + ## BUGS The template content is handled by handwritten parsers, that even being well -- cgit v1.2.3-18-g5258 From 8e01ccec4837027a12a4bfc6a54e3c09440169a9 Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Thu, 14 Jan 2016 21:44:52 +0100 Subject: Revert "build: added git-version-gen support" This reverts commit 5e53ba7406e65b51ec59aad634b6baf7154e1ad3. --- .gitignore | 4 - Makefile.am | 15 ---- build-aux/git-version-gen | 225 ---------------------------------------------- configure.ac | 4 +- 4 files changed, 2 insertions(+), 246 deletions(-) delete mode 100755 build-aux/git-version-gen diff --git a/.gitignore b/.gitignore index 01d2647..2ccfd38 100644 --- a/.gitignore +++ b/.gitignore @@ -33,7 +33,6 @@ Makefile.in .dirstamp /build-aux/* !/build-aux/build-windows.sh -!/build-aux/git-version-gen # installed .m4 files /m4/*.m4 @@ -63,6 +62,3 @@ blogc-*.zip # rpms blogc.spec blogc-*.rpm - -# git-version-gen -/.version diff --git a/Makefile.am b/Makefile.am index 7b52fa0..e143b0b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -13,8 +13,6 @@ AM_DISTCHECK_CONFIGURE_FLAGS = \ EXTRA_DIST = \ build-aux/build-windows.sh \ - build-aux/git-version-gen \ - $(top_srcdir)/.version \ autogen.sh \ LICENSE \ README.md \ @@ -26,10 +24,6 @@ CLEANFILES = \ MAINTAINERCLEANFILES = \ $(NULL) -BUILT_SOURCES = \ - $(top_srcdir)/.version \ - $(NULL) - noinst_HEADERS = \ src/content-parser.h \ src/datetime-parser.h \ @@ -314,15 +308,6 @@ TESTS = \ $(check_PROGRAMS) -## Helpers: git-version-gen - -$(top_srcdir)/.version: - echo $(VERSION) > $@-t && mv $@-t $@ - -dist-hook: - echo $(VERSION) > $(distdir)/.tarball-version - - ## Helpers: Valgrind runner if USE_VALGRIND diff --git a/build-aux/git-version-gen b/build-aux/git-version-gen deleted file mode 100755 index 3468247..0000000 --- a/build-aux/git-version-gen +++ /dev/null @@ -1,225 +0,0 @@ -#!/bin/sh -# Print a version string. -scriptversion=2012-12-31.23; # UTC - -# Copyright (C) 2007-2013 Free Software Foundation, Inc. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/. -# It may be run two ways: -# - from a git repository in which the "git describe" command below -# produces useful output (thus requiring at least one signed tag) -# - from a non-git-repo directory containing a .tarball-version file, which -# presumes this script is invoked like "./git-version-gen .tarball-version". - -# In order to use intra-version strings in your project, you will need two -# separate generated version string files: -# -# .tarball-version - present only in a distribution tarball, and not in -# a checked-out repository. Created with contents that were learned at -# the last time autoconf was run, and used by git-version-gen. Must not -# be present in either $(srcdir) or $(builddir) for git-version-gen to -# give accurate answers during normal development with a checked out tree, -# but must be present in a tarball when there is no version control system. -# Therefore, it cannot be used in any dependencies. GNUmakefile has -# hooks to force a reconfigure at distribution time to get the value -# correct, without penalizing normal development with extra reconfigures. -# -# .version - present in a checked-out repository and in a distribution -# tarball. Usable in dependencies, particularly for files that don't -# want to depend on config.h but do want to track version changes. -# Delete this file prior to any autoconf run where you want to rebuild -# files to pick up a version string change; and leave it stale to -# minimize rebuild time after unrelated changes to configure sources. -# -# As with any generated file in a VC'd directory, you should add -# /.version to .gitignore, so that you don't accidentally commit it. -# .tarball-version is never generated in a VC'd directory, so needn't -# be listed there. -# -# Use the following line in your configure.ac, so that $(VERSION) will -# automatically be up-to-date each time configure is run (and note that -# since configure.ac no longer includes a version string, Makefile rules -# should not depend on configure.ac for version updates). -# -# AC_INIT([GNU project], -# m4_esyscmd([build-aux/git-version-gen .tarball-version]), -# [bug-project@example]) -# -# Then use the following lines in your Makefile.am, so that .version -# will be present for dependencies, and so that .version and -# .tarball-version will exist in distribution tarballs. -# -# EXTRA_DIST = $(top_srcdir)/.version -# BUILT_SOURCES = $(top_srcdir)/.version -# $(top_srcdir)/.version: -# echo $(VERSION) > $@-t && mv $@-t $@ -# dist-hook: -# echo $(VERSION) > $(distdir)/.tarball-version - - -me=$0 - -version="git-version-gen $scriptversion - -Copyright 2011 Free Software Foundation, Inc. -There is NO warranty. You may redistribute this software -under the terms of the GNU General Public License. -For more information about these matters, see the files named COPYING." - -usage="\ -Usage: $me [OPTION]... \$srcdir/.tarball-version [TAG-NORMALIZATION-SED-SCRIPT] -Print a version string. - -Options: - - --prefix prefix of git tags (default 'v') - --fallback fallback version to use if \"git --version\" fails - - --help display this help and exit - --version output version information and exit - -Running without arguments will suffice in most cases." - -prefix=v -fallback= - -while test $# -gt 0; do - case $1 in - --help) echo "$usage"; exit 0;; - --version) echo "$version"; exit 0;; - --prefix) shift; prefix="$1";; - --fallback) shift; fallback="$1";; - -*) - echo "$0: Unknown option '$1'." >&2 - echo "$0: Try '--help' for more information." >&2 - exit 1;; - *) - if test "x$tarball_version_file" = x; then - tarball_version_file="$1" - elif test "x$tag_sed_script" = x; then - tag_sed_script="$1" - else - echo "$0: extra non-option argument '$1'." >&2 - exit 1 - fi;; - esac - shift -done - -if test "x$tarball_version_file" = x; then - echo "$usage" - exit 1 -fi - -tag_sed_script="${tag_sed_script:-s/x/x/}" - -nl=' -' - -# Avoid meddling by environment variable of the same name. -v= -v_from_git= - -# First see if there is a tarball-only version file. -# then try "git describe", then default. -if test -f $tarball_version_file -then - v=`cat $tarball_version_file` || v= - case $v in - *$nl*) v= ;; # reject multi-line output - [0-9]*) ;; - *) v= ;; - esac - test "x$v" = x \ - && echo "$0: WARNING: $tarball_version_file is missing or damaged" 1>&2 -fi - -if test "x$v" != x -then - : # use $v -# Otherwise, if there is at least one git commit involving the working -# directory, and "git describe" output looks sensible, use that to -# derive a version string. -elif test "`git log -1 --pretty=format:x . 2>&1`" = x \ - && v=`git describe --abbrev=4 --match="$prefix*" HEAD 2>/dev/null \ - || git describe --abbrev=4 HEAD 2>/dev/null` \ - && v=`printf '%s\n' "$v" | sed "$tag_sed_script"` \ - && case $v in - $prefix[0-9]*) ;; - *) (exit 1) ;; - esac -then - # Is this a new git that lists number of commits since the last - # tag or the previous older version that did not? - # Newer: v6.10-77-g0f8faeb - # Older: v6.10-g0f8faeb - case $v in - *-*-*) : git describe is okay three part flavor ;; - *-*) - : git describe is older two part flavor - # Recreate the number of commits and rewrite such that the - # result is the same as if we were using the newer version - # of git describe. - vtag=`echo "$v" | sed 's/-.*//'` - commit_list=`git rev-list "$vtag"..HEAD 2>/dev/null` \ - || { commit_list=failed; - echo "$0: WARNING: git rev-list failed" 1>&2; } - numcommits=`echo "$commit_list" | wc -l` - v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`; - test "$commit_list" = failed && v=UNKNOWN - ;; - esac - - # Change the first '-' to a '.', so version-comparing tools work properly. - # Remove the "g" in git describe's output string, to save a byte. - v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`; - v_from_git=1 -elif test "x$fallback" = x || git --version >/dev/null 2>&1; then - v=UNKNOWN -else - v=$fallback -fi - -v=`echo "$v" |sed "s/^$prefix//"` - -# Test whether to append the "-dirty" suffix only if the version -# string we're using came from git. I.e., skip the test if it's "UNKNOWN" -# or if it came from .tarball-version. -if test "x$v_from_git" != x; then - # Don't declare a version "dirty" merely because a time stamp has changed. - git update-index --refresh > /dev/null 2>&1 - - dirty=`exec 2>/dev/null;git diff-index --name-only HEAD` || dirty= - case "$dirty" in - '') ;; - *) # Append the suffix only if there isn't one already. - case $v in - *-dirty) ;; - *) v="$v-dirty" ;; - esac ;; - esac -fi - -# Omit the trailing newline, so that m4_esyscmd can use the result directly. -echo "$v" | tr -d "$nl" - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "scriptversion=" -# time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" -# time-stamp-end: "; # UTC" -# End: diff --git a/configure.ac b/configure.ac index f837f1b..e43633d 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ AC_PREREQ([2.69]) -AC_INIT([blogc], m4_esyscmd([build-aux/git-version-gen .tarball-version]), - [https://github.com/blogc/blogc], [blogc], [https://blogc.rgm.io]) +AC_INIT([blogc], [0.6.1], [https://github.com/blogc/blogc], [blogc], + [https://blogc.rgm.io]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) -- cgit v1.2.3-18-g5258 From 0400a1a365b67e2bd9acc6fe4e5fb4d1ec2cf825 Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Thu, 14 Jan 2016 22:55:49 +0100 Subject: build: version bump --- blogc.spec.in | 3 +++ configure.ac | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/blogc.spec.in b/blogc.spec.in index f10e7fc..e185379 100644 --- a/blogc.spec.in +++ b/blogc.spec.in @@ -38,6 +38,9 @@ rm -rf $RPM_BUILD_ROOT %changelog +* Thu Jan 14 2016 Rafael G. Martins 0.7-1 +- New release. + * Sun Jan 10 2016 Rafael G. Martins 0.6.1-1 - New release. diff --git a/configure.ac b/configure.ac index e43633d..cd52b20 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ AC_PREREQ([2.69]) -AC_INIT([blogc], [0.6.1], [https://github.com/blogc/blogc], [blogc], +AC_INIT([blogc], [0.7], [https://github.com/blogc/blogc], [blogc], [https://blogc.rgm.io]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) -- cgit v1.2.3-18-g5258 From 06d8ca94860f4a795995d811cef477969baa2331 Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Sat, 16 Jan 2016 18:42:05 +0100 Subject: build: spec: do not hardcode package name --- blogc.spec.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blogc.spec.in b/blogc.spec.in index e185379..b66d92a 100644 --- a/blogc.spec.in +++ b/blogc.spec.in @@ -1,4 +1,4 @@ -Name: blogc +Name: @PACKAGE_NAME@ Version: @PACKAGE_VERSION@ Release: 1%{?dist} License: BSD -- cgit v1.2.3-18-g5258 From cb132cf02e57f57f4507fbc0126481629d83f209 Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Fri, 22 Jan 2016 19:29:57 +0100 Subject: content-parser: encode html entities found in code blocks (fixes #3) --- src/content-parser.c | 40 ++++++++++++++++++++++++++++++++++++++-- src/content-parser.h | 1 + tests/check_content_parser.c | 20 ++++++++++++++++---- 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/src/content-parser.c b/src/content-parser.c index b22eb70..5b85586 100644 --- a/src/content-parser.c +++ b/src/content-parser.c @@ -41,6 +41,40 @@ blogc_slugify(const char *str) } +char* +blogc_htmlentities(const char *str) +{ + if (str == NULL) + return NULL; + b_string_t *rv = b_string_new(); + for (unsigned int i = 0; str[i] != '\0'; i++) { + switch (str[i]) { + case '&': + b_string_append(rv, "&"); + break; + case '<': + b_string_append(rv, "<"); + break; + case '>': + b_string_append(rv, ">"); + break; + case '"': + b_string_append(rv, """); + break; + case '\'': + b_string_append(rv, "'"); + break; + case '/': + b_string_append(rv, "/"); + break; + default: + b_string_append_c(rv, str[i]); + } + } + return b_string_free(rv, false); +} + + typedef enum { CONTENT_START_LINE = 1, CONTENT_EXCERPT, @@ -698,11 +732,13 @@ blogc_content_parse(const char *src, size_t *end_excerpt) if (c == '\n' || c == '\r' || is_last) { b_string_append(rv, "
");
                     for (b_slist_t *l = lines; l != NULL; l = l->next) {
+                        char *tmp_line = blogc_htmlentities(l->data);
                         if (l->next == NULL)
-                            b_string_append_printf(rv, "%s", l->data);
+                            b_string_append_printf(rv, "%s", tmp_line);
                         else
-                            b_string_append_printf(rv, "%s%s", l->data,
+                            b_string_append_printf(rv, "%s%s", tmp_line,
                                 line_ending);
+                        free(tmp_line);
                     }
                     b_string_append_printf(rv, "
%s", line_ending); b_slist_free_full(lines, free); diff --git a/src/content-parser.h b/src/content-parser.h index 5802594..6617bb4 100644 --- a/src/content-parser.h +++ b/src/content-parser.h @@ -13,6 +13,7 @@ #include char* blogc_slugify(const char *str); +char* blogc_htmlentities(const char *str); char* blogc_content_parse_inline(const char *src); bool blogc_is_ordered_list_item(const char *str, size_t prefix_len); char* blogc_content_parse(const char *src, size_t *end_excerpt); diff --git a/tests/check_content_parser.c b/tests/check_content_parser.c index 8ed9520..970ec5c 100644 --- a/tests/check_content_parser.c +++ b/tests/check_content_parser.c @@ -48,6 +48,17 @@ test_slugify(void **state) } +static void +test_htmlentities(void **state) +{ + char *s = blogc_htmlentities(NULL); + assert_null(s); + s = blogc_htmlentities("asdxcv & < > \" 'sfd/gf"); + assert_string_equal(s, "asdxcv & < > " 'sfd/gf"); + free(s); +} + + static void test_is_ordered_list_item(void **state) { @@ -87,7 +98,7 @@ test_content_parse(void **state) "> \n" "> asd\n" "\n" - " bola\n" + " bola\n" " asd\n" " qwewer\n" "\n" @@ -122,7 +133,7 @@ test_content_parse(void **state) "buga

\n" "
asd
\n" "\n" - "
bola\n"
+        "
<asd>bola</asd>\n"
         " asd\n"
         "qwewer
\n" "
\n" @@ -165,7 +176,7 @@ test_content_parse_crlf(void **state) "> \r\n" "> asd\r\n" "\r\n" - " bola\r\n" + " bola\r\n" " asd\r\n" " qwewer\r\n" "\r\n" @@ -200,7 +211,7 @@ test_content_parse_crlf(void **state) "buga

\r\n" "
asd
\r\n" "\r\n" - "
bola\r\n"
+        "
<asd>bola</asd>\r\n"
         " asd\r\n"
         "qwewer
\r\n" "
\r\n" @@ -1663,6 +1674,7 @@ main(void) { const UnitTest tests[] = { unit_test(test_slugify), + unit_test(test_htmlentities), unit_test(test_is_ordered_list_item), unit_test(test_content_parse), unit_test(test_content_parse_crlf), -- cgit v1.2.3-18-g5258 From 254b2f23440cb9360cf2e19906d0b56256412e26 Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Fri, 22 Jan 2016 19:57:24 +0100 Subject: content-parser: use size_t instead of unsigned len when handling strings --- src/content-parser.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/content-parser.c b/src/content-parser.c index 5b85586..1a929e2 100644 --- a/src/content-parser.c +++ b/src/content-parser.c @@ -27,7 +27,7 @@ blogc_slugify(const char *str) return NULL; char *new_str = b_strdup(str); int diff = 'a' - 'A'; // just to avoid magic numbers - for (unsigned int i = 0; new_str[i] != '\0'; i++) { + for (size_t i = 0; new_str[i] != '\0'; i++) { if (new_str[i] >= 'a' && new_str[i] <= 'z') continue; if (new_str[i] >= '0' && new_str[i] <= '9') @@ -47,7 +47,7 @@ blogc_htmlentities(const char *str) if (str == NULL) return NULL; b_string_t *rv = b_string_new(); - for (unsigned int i = 0; str[i] != '\0'; i++) { + for (size_t i = 0; str[i] != '\0'; i++) { switch (str[i]) { case '&': b_string_append(rv, "&"); -- cgit v1.2.3-18-g5258 From c7297cb5e74727c136547f588503e488ca40dd30 Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Fri, 22 Jan 2016 20:32:30 +0100 Subject: build: version bump --- blogc.spec.in | 3 +++ configure.ac | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/blogc.spec.in b/blogc.spec.in index b66d92a..53364f9 100644 --- a/blogc.spec.in +++ b/blogc.spec.in @@ -38,6 +38,9 @@ rm -rf $RPM_BUILD_ROOT %changelog +* Fri Jan 22 2016 Rafael G. Martins 0.7.1-1 +- New release. + * Thu Jan 14 2016 Rafael G. Martins 0.7-1 - New release. diff --git a/configure.ac b/configure.ac index cd52b20..4303f97 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ AC_PREREQ([2.69]) -AC_INIT([blogc], [0.7], [https://github.com/blogc/blogc], [blogc], +AC_INIT([blogc], [0.7.1], [https://github.com/blogc/blogc], [blogc], [https://blogc.rgm.io]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) -- cgit v1.2.3-18-g5258 From 35c4f119ca188fd9e90a2bbada864f9d121459ba Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Mon, 25 Jan 2016 04:46:19 +0100 Subject: renderer: fix bug when 'if' evals to false after 'if' evals to true --- src/renderer.c | 10 +++++----- tests/check_renderer.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/renderer.c b/src/renderer.c index 0f5d10a..5e07b0c 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -134,7 +134,6 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing char *defined = NULL; unsigned int if_count = 0; - unsigned int if_skip = 0; b_slist_t *foreach_var = NULL; b_slist_t *foreach_var_start = NULL; @@ -237,6 +236,7 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing case BLOGC_TEMPLATE_IF_STMT: case BLOGC_TEMPLATE_IFDEF_STMT: + if_count = 0; defined = NULL; if (stmt->value != NULL) defined = blogc_format_variable(stmt->value, config, @@ -283,7 +283,6 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing evaluate = true; } if (!evaluate) { - if_skip = if_count; // at this point we can just skip anything, counting the // number of 'if's, to know how many 'endif's we need to @@ -299,11 +298,11 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing continue; } if (stmt->type == BLOGC_TEMPLATE_ENDIF_STMT) { - if (if_count > if_skip) { + if (if_count > 0) { if_count--; continue; } - if (if_count == if_skip) + if (if_count == 0) break; } } @@ -314,7 +313,8 @@ blogc_render(b_slist_t *tmpl, b_slist_t *sources, b_trie_t *config, bool listing break; case BLOGC_TEMPLATE_ENDIF_STMT: - if_count--; + if (if_count > 0) + if_count--; break; case BLOGC_TEMPLATE_FOREACH_STMT: diff --git a/tests/check_renderer.c b/tests/check_renderer.c index 360f067..d6bc947 100644 --- a/tests/check_renderer.c +++ b/tests/check_renderer.c @@ -686,6 +686,37 @@ test_render_respect_variable_scope(void **state) } +static void +test_render_ifcount_bug(void **state) +{ + const char *str = + "{% block entry %}\n" + "{% ifdef TITLE %}

{{ TITLE }}

{% endif %}\n" + "{% ifdef IS_POST %}\n" + "{% ifdef ASD %}ASD{% endif %}\n" + "{% endif %}\n" + "{% endblock %}\n"; + blogc_error_t *err = NULL; + b_slist_t *l = blogc_template_parse(str, strlen(str), &err); + assert_non_null(l); + assert_null(err); + b_slist_t *s = NULL; + s = b_slist_append(s, b_trie_new(free)); + b_trie_insert(s->data, "TITLE", b_strdup("bola")); + b_trie_t *c = b_trie_new(free); + char *out = blogc_render(l, s, c, false); + assert_string_equal(out, + "\n" + "

bola

\n" + "\n" + "\n"); + b_trie_free(c); + blogc_template_free_stmts(l); + b_slist_free_full(s, (b_free_func_t) b_trie_free); + free(out); +} + + static void test_get_variable(void **state) { @@ -897,6 +928,7 @@ main(void) unit_test(test_render_outside_block), unit_test(test_render_prefer_local_variable), unit_test(test_render_respect_variable_scope), + unit_test(test_render_ifcount_bug), unit_test(test_get_variable), unit_test(test_get_variable_only_local), unit_test(test_get_variable_only_global), -- cgit v1.2.3-18-g5258 From 3b0ea0a4908f1702308354ec7cbd8db5c80f87ee Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Mon, 25 Jan 2016 20:07:57 +0100 Subject: build: version bump --- blogc.spec.in | 3 +++ configure.ac | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/blogc.spec.in b/blogc.spec.in index 53364f9..3fa3353 100644 --- a/blogc.spec.in +++ b/blogc.spec.in @@ -38,6 +38,9 @@ rm -rf $RPM_BUILD_ROOT %changelog +* Mon Jan 25 2016 Rafael G. Martins 0.7.2-1 +- New release. + * Fri Jan 22 2016 Rafael G. Martins 0.7.1-1 - New release. diff --git a/configure.ac b/configure.ac index 4303f97..9ceed22 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ AC_PREREQ([2.69]) -AC_INIT([blogc], [0.7.1], [https://github.com/blogc/blogc], [blogc], +AC_INIT([blogc], [0.7.2], [https://github.com/blogc/blogc], [blogc], [https://blogc.rgm.io]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) -- cgit v1.2.3-18-g5258