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 --- src/renderer.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/renderer.h | 2 ++ src/template-parser.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++- src/template-parser.h | 2 ++ 4 files changed, 141 insertions(+), 1 deletion(-) (limited to 'src') 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, -- 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 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'src') 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: -- 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 --- src/renderer.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src') 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; } -- 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 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src') 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); -- 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 ++- 2 files changed, 15 insertions(+), 10 deletions(-) (limited to 'src') 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, -- 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 --- 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 +- 22 files changed, 22 insertions(+), 22 deletions(-) (limited to 'src') 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. -- 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 ++ 4 files changed, 129 insertions(+), 22 deletions(-) (limited to 'src') 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); -- 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(-) (limited to 'src') 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 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 + 2 files changed, 39 insertions(+), 2 deletions(-) (limited to 'src') 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); -- 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(-) (limited to 'src') 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 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 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') 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: -- cgit v1.2.3-18-g5258