From e52e1f0b9d0796412ee36de260c6bbbcde348b0a Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Sat, 11 Feb 2023 22:45:04 +0100 Subject: blogc: renderer: add `FOREACH_VALUE` variable --- man/blogc-template.7.ronn | 34 +++++++++++++++++++++---- src/blogc/main.c | 2 +- src/blogc/renderer.c | 58 ++++++++++++++++++++++++++++++++++++++---- src/blogc/renderer.h | 2 +- tests/blogc/check_renderer.c | 60 +++++++++++++++++++++++++++++++------------- 5 files changed, 127 insertions(+), 29 deletions(-) diff --git a/man/blogc-template.7.ronn b/man/blogc-template.7.ronn index 5184941..1530fb3 100644 --- a/man/blogc-template.7.ronn +++ b/man/blogc-template.7.ronn @@ -317,16 +317,40 @@ This is how a `foreach` iterator is defined in a template: {% endforeach %} 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. +is the variable defined by blogc(1), that will store the item for a given iteration. If the value of the `TAGS` variable is "item1 item2 item3", this template is -rendered 3 times, one for each item value. +rendered 3 times, one for each item. -The `FOREACH_ITEM` variable can be truncated, like: +It is possible to map the items to arbitrary strings by defining other variables +following a predefined for naming schema, like: + + TAGS__ITEM1 + +Where `TAGS` is the variable defined in the foreach iterator, and `ITEM1` is the +current iteration item (the value of `FOREACH_ITEM` for the current iteration, +converted to upper case and with special characters converted to `_`). + +This is how a `foreach` iterator using arbitrary string mapping is defined in a template: + + {% foreach TAGS %} + {{ FOREACH_VALUE }} + {% endforeach %} + +`FOREACH_VALUE` is the variable defined by blogc(1), that will store the arbitrary string +mapped to current iteration item. This variable can be checked using `ifdef` conditionals +(and similar) as usual: + + {% foreach TAGS %} + {% ifdef FOREACH_VALUE %} + {{ FOREACH_VALUE }} + {% endif %} + {% endforeach %} + +The `FOREACH_ITEM` and `FOREACH_VALUE` variables can be truncated, like: {% foreach TAGS %} - {{ FOREACH_ITEM_5 }} + {{ FOREACH_VALUE_5 }} {% endforeach %} ## WHITESPACE CONTROL diff --git a/src/blogc/main.c b/src/blogc/main.c index 7024967..9bfa9cf 100644 --- a/src/blogc/main.c +++ b/src/blogc/main.c @@ -322,7 +322,7 @@ main(int argc, char **argv) if (!listing && s != NULL) { local = s->data; } - char *val = blogc_format_variable(print, config, local, NULL); + char *val = blogc_format_variable(print, config, local, NULL, NULL); if (val == NULL) { fprintf(stderr, "blogc: error: variable not found: %s\n", print); diff --git a/src/blogc/renderer.c b/src/blogc/renderer.c index 785c77b..a91d105 100644 --- a/src/blogc/renderer.c +++ b/src/blogc/renderer.c @@ -54,22 +54,57 @@ blogc_format_date(const char *date, bc_trie_t *global, bc_trie_t *local) } +static char* +foreach_value_variable(const char *name, const char *item) +{ + if (name == NULL || item == NULL) + return NULL; + + char *rv = bc_strdup_printf("%s__%s", name, item); + int diff = 'a' - 'A'; // just to avoid magic numbers + for (size_t i = 0; rv[i] != '\0'; i++) { + if ((rv[i] >= '0' && rv[i] <= '9') || + (rv[i] >= 'A' && rv[i] <= 'Z')) { + continue; + } + if (rv[i] >= 'a' && rv[i] <= 'z') { + rv[i] -= diff; + continue; + } + rv[i] = '_'; + } + + return rv; +} + + char* blogc_format_variable(const char *name, bc_trie_t *global, bc_trie_t *local, - bc_slist_t *foreach_var) + const char *foreach_name, bc_slist_t *foreach_var) { // if used asked for a variable that exists, just return it right away const char *value = blogc_get_variable(name, global, local); if (value != NULL) return bc_strdup(value); - // do the same for special variable 'FOREACH_ITEM' + // do the same for special foreach variables if (0 == strcmp(name, "FOREACH_ITEM")) { if (foreach_var != NULL && foreach_var->data != NULL) { return bc_strdup(foreach_var->data); } return NULL; } + if (0 == strcmp(name, "FOREACH_VALUE")) { + if (foreach_name != NULL && foreach_var != NULL && foreach_var->data != NULL) { + char *value_var = foreach_value_variable(foreach_name, foreach_var->data); + if (value_var != NULL) { + value = blogc_get_variable(value_var, global, local); + free(value_var); + return bc_strdup(value); + } + } + return NULL; + } char *var = bc_strdup(name); @@ -105,6 +140,14 @@ blogc_format_variable(const char *name, bc_trie_t *global, bc_trie_t *local, (foreach_var != NULL && foreach_var->data != NULL)) { value = foreach_var->data; } + else if ((0 == strcmp(var, "FOREACH_VALUE")) && + (foreach_name != NULL && foreach_var != NULL && foreach_var->data != NULL)) { + char *value_var = foreach_value_variable(foreach_name, foreach_var->data); + if (value_var != NULL) { + value = blogc_get_variable(value_var, global, local); + free(value_var); + } + } else { blogc_funcvars_eval(global, var); value = blogc_get_variable(var, global, local); @@ -183,6 +226,7 @@ blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_slist_t *listing_entries, size_t if_count = 0; + char *foreach_name = NULL; bc_slist_t *foreach_var = NULL; bc_slist_t *foreach_var_start = NULL; bc_slist_t *foreach_start = NULL; @@ -289,7 +333,7 @@ blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_slist_t *listing_entries, case BLOGC_TEMPLATE_NODE_VARIABLE: if (node->data[0] != NULL) { config_value = blogc_format_variable(node->data[0], - config, inside_block ? tmp_source : NULL, foreach_var); + config, inside_block ? tmp_source : NULL, foreach_name, foreach_var); if (config_value != NULL) { bc_string_append(str, config_value); free(config_value); @@ -321,7 +365,7 @@ blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_slist_t *listing_entries, defined = NULL; if (node->data[0] != NULL) defined = blogc_format_variable(node->data[0], config, - inside_block ? tmp_source : NULL, foreach_var); + inside_block ? tmp_source : NULL, foreach_name, foreach_var); evaluate = false; if (node->op != 0) { // Strings that start with a '"' are actually strings, the @@ -339,7 +383,7 @@ blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_slist_t *listing_entries, else { defined2 = blogc_format_variable(node->data[1], config, inside_block ? tmp_source : NULL, - foreach_var); + foreach_name, foreach_var); } } @@ -452,6 +496,7 @@ blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_slist_t *listing_entries, config, inside_block ? tmp_source : NULL); if (foreach_var_start != NULL) { + foreach_name = bc_strdup(node->data[0]); foreach_var = foreach_var_start; foreach_start = tmp; } @@ -469,6 +514,7 @@ blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_slist_t *listing_entries, if (foreach_var == NULL) { foreach_start = tmp; + foreach_name = bc_strdup(node->data[0]); foreach_var = foreach_var_start; } break; @@ -484,6 +530,8 @@ blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_slist_t *listing_entries, foreach_start = NULL; bc_slist_free_full(foreach_var_start, free); foreach_var_start = NULL; + free(foreach_name); + foreach_name = NULL; break; } tmp = tmp->next; diff --git a/src/blogc/renderer.h b/src/blogc/renderer.h index 77f20f5..77c660a 100644 --- a/src/blogc/renderer.h +++ b/src/blogc/renderer.h @@ -15,7 +15,7 @@ const char* blogc_get_variable(const char *name, bc_trie_t *global, bc_trie_t *local); char* blogc_format_date(const char *date, bc_trie_t *global, bc_trie_t *local); char* blogc_format_variable(const char *name, bc_trie_t *global, bc_trie_t *local, - bc_slist_t *foreach_var); + const char *foreach_name, bc_slist_t *foreach_var); bc_slist_t* blogc_split_list_variable(const char *name, bc_trie_t *global, bc_trie_t *local); char* blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_slist_t *listing_entries, diff --git a/tests/blogc/check_renderer.c b/tests/blogc/check_renderer.c index a5d6729..08aed84 100644 --- a/tests/blogc/check_renderer.c +++ b/tests/blogc/check_renderer.c @@ -1253,23 +1253,23 @@ test_format_variable(void **state) bc_trie_insert(l, "NAME", bc_strdup("chunda")); bc_trie_insert(l, "TITLE", bc_strdup("chunda2")); bc_trie_insert(l, "SIZE", bc_strdup("1234567890987654321")); - char *tmp = blogc_format_variable("NAME", g, l, NULL); + char *tmp = blogc_format_variable("NAME", g, l, NULL, NULL); assert_string_equal(tmp, "chunda"); free(tmp); - tmp = blogc_format_variable("TITLE", g, l, NULL); + tmp = blogc_format_variable("TITLE", g, l, NULL, NULL); assert_string_equal(tmp, "chunda2"); free(tmp); - tmp = blogc_format_variable("TITLE_2", g, l, NULL); + tmp = blogc_format_variable("TITLE_2", g, l, NULL, NULL); assert_string_equal(tmp, "ch"); free(tmp); - tmp = blogc_format_variable("SIZE_12", g, l, NULL); + tmp = blogc_format_variable("SIZE_12", g, l, NULL, NULL); assert_string_equal(tmp, "123456789098"); free(tmp); - tmp = blogc_format_variable("SIZE_200", g, l, NULL); + tmp = blogc_format_variable("SIZE_200", g, l, NULL, NULL); assert_string_equal(tmp, "1234567890987654321"); free(tmp); - assert_null(blogc_format_variable("SIZE_", g, l, NULL)); - assert_null(blogc_format_variable("BOLA", g, l, NULL)); + assert_null(blogc_format_variable("SIZE_", g, l, NULL, NULL)); + assert_null(blogc_format_variable("BOLA", g, l, NULL, NULL)); bc_trie_free(g); bc_trie_free(l); } @@ -1283,13 +1283,13 @@ test_format_variable_with_date(void **state) bc_trie_insert(g, "DATE_FORMAT", bc_strdup("%R")); bc_trie_t *l = bc_trie_new(free); bc_trie_insert(l, "DATE", bc_strdup("2011-12-13 14:15:16")); - char *tmp = blogc_format_variable("DATE_FORMATTED", g, l, NULL); + char *tmp = blogc_format_variable("DATE_FORMATTED", g, l, NULL, NULL); assert_string_equal(tmp, "14:15"); free(tmp); - tmp = blogc_format_variable("DATE_FORMATTED_3", g, l, NULL); + tmp = blogc_format_variable("DATE_FORMATTED_3", g, l, NULL, NULL); assert_string_equal(tmp, "14:"); free(tmp); - tmp = blogc_format_variable("DATE_FORMATTED_10", g, l, NULL); + tmp = blogc_format_variable("DATE_FORMATTED_10", g, l, NULL, NULL); assert_string_equal(tmp, "14:15"); free(tmp); bc_trie_free(g); @@ -1304,26 +1304,51 @@ test_format_variable_foreach(void **state) l = bc_slist_append(l, bc_strdup("asd")); l = bc_slist_append(l, bc_strdup("qwe")); l = bc_slist_append(l, bc_strdup("zxcvbn")); - char *tmp = blogc_format_variable("FOREACH_ITEM", NULL, NULL, l->next); + char *tmp = blogc_format_variable("FOREACH_ITEM", NULL, NULL, NULL, l->next); assert_string_equal(tmp, "qwe"); free(tmp); - tmp = blogc_format_variable("FOREACH_ITEM_4", NULL, NULL, - l->next->next); + tmp = blogc_format_variable("FOREACH_ITEM_4", NULL, NULL, NULL, l->next->next); assert_string_equal(tmp, "zxcv"); free(tmp); - tmp = blogc_format_variable("FOREACH_ITEM_10", NULL, NULL, - l->next->next); + tmp = blogc_format_variable("FOREACH_ITEM_10", NULL, NULL, NULL, l->next->next); assert_string_equal(tmp, "zxcvbn"); free(tmp); bc_slist_free_full(l, free); } +static void +test_format_variable_foreach_value(void **state) +{ + bc_trie_t *gl = bc_trie_new(free); + bc_trie_insert(gl, "FOO_BAR__QWE", bc_strdup("bnm")); + bc_trie_t *loc = bc_trie_new(free); + bc_trie_insert(loc, "BAR__ZXCVBN", bc_strdup("dfg")); + bc_trie_insert(loc, "HUE__ZXCVBN", bc_strdup("xcv")); + bc_slist_t *l = NULL; + l = bc_slist_append(l, bc_strdup("asd")); + l = bc_slist_append(l, bc_strdup("qwe")); + l = bc_slist_append(l, bc_strdup("zxcvbn")); + char *tmp = blogc_format_variable("FOREACH_VALUE", gl, loc, "foo-bar", l->next); + assert_string_equal(tmp, "bnm"); + free(tmp); + tmp = blogc_format_variable("FOREACH_VALUE_2", gl, loc, "bar", l->next->next); + assert_string_equal(tmp, "df"); + free(tmp); + tmp = blogc_format_variable("FOREACH_VALUE_4", gl, loc, "hue", l->next->next); + assert_string_equal(tmp, "xcv"); + free(tmp); + bc_trie_free(gl); + bc_trie_free(loc); + bc_slist_free_full(l, free); +} + + static void test_format_variable_foreach_empty(void **state) { - assert_null(blogc_format_variable("FOREACH_ITEM", NULL, NULL, NULL)); - assert_null(blogc_format_variable("FOREACH_ITEM_4", NULL, NULL, NULL)); + assert_null(blogc_format_variable("FOREACH_ITEM", NULL, NULL, NULL, NULL)); + assert_null(blogc_format_variable("FOREACH_ITEM_4", NULL, NULL, NULL, NULL)); } @@ -1401,6 +1426,7 @@ main(void) cmocka_unit_test(test_format_variable), cmocka_unit_test(test_format_variable_with_date), cmocka_unit_test(test_format_variable_foreach), + cmocka_unit_test(test_format_variable_foreach_value), cmocka_unit_test(test_format_variable_foreach_empty), cmocka_unit_test(test_split_list_variable), cmocka_unit_test(test_split_list_variable_not_found), -- cgit v1.2.3-18-g5258