aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael G. Martins <rafael@rafaelmartins.eng.br>2019-02-09 02:29:05 +0100
committerRafael G. Martins <rafael@rafaelmartins.eng.br>2019-02-09 02:29:05 +0100
commit49ed30a5e87e510fd0c14b367110175f9d2fb144 (patch)
tree348319b8696aa917813c185b1e2e0425b44bf4f9
parent26325e3e684dc87b9751750d2a98ba6a61f8f055 (diff)
downloadblogc-49ed30a5e87e510fd0c14b367110175f9d2fb144.tar.gz
blogc-49ed30a5e87e510fd0c14b367110175f9d2fb144.tar.bz2
blogc-49ed30a5e87e510fd0c14b367110175f9d2fb144.zip
blogc: template: allow whitespaces in template tags, not just spaces
-rw-r--r--src/blogc/template-parser.c31
-rw-r--r--src/common/utils.c8
-rw-r--r--src/common/utils.h1
-rw-r--r--tests/blogc/check_template_parser.c130
4 files changed, 154 insertions, 16 deletions
diff --git a/src/blogc/template-parser.c b/src/blogc/template-parser.c
index c3e9a5c..a53f1aa 100644
--- a/src/blogc/template-parser.c
+++ b/src/blogc/template-parser.c
@@ -161,7 +161,7 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
state = TEMPLATE_BLOCK_START;
case TEMPLATE_BLOCK_START:
- if (c == ' ')
+ if (bc_isspace(c))
break;
if (c >= 'a' && c <= 'z') {
state = TEMPLATE_BLOCK_TYPE;
@@ -183,7 +183,7 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
case TEMPLATE_BLOCK_TYPE:
if (c >= 'a' && c <= 'z')
break;
- if (c == ' ') {
+ if (bc_isspace(c)) {
if ((current - start == 5) &&
(0 == strncmp("block", src + start, 5)))
{
@@ -339,7 +339,7 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
break;
case TEMPLATE_BLOCK_BLOCK_TYPE_START:
- if (c == ' ')
+ if (bc_isspace(c))
break;
if (c >= 'a' && c <= 'z') {
state = TEMPLATE_BLOCK_BLOCK_TYPE;
@@ -354,7 +354,7 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
case TEMPLATE_BLOCK_BLOCK_TYPE:
if ((c >= 'a' && c <= 'z') || c == '_')
break;
- if (c == ' ') {
+ if (bc_isspace(c)) {
if ((current - start == 5) &&
(0 == strncmp("entry", src + start, 5)))
{
@@ -387,7 +387,7 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
break;
case TEMPLATE_BLOCK_IF_START:
- if (c == ' ')
+ if (bc_isspace(c))
break;
if (c >= 'A' && c <= 'Z') {
state = TEMPLATE_BLOCK_IF_VARIABLE;
@@ -402,7 +402,7 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
case TEMPLATE_BLOCK_IF_VARIABLE:
if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')
break;
- if (c == ' ') {
+ if (bc_isspace(c)) {
end = current;
if (type == BLOGC_TEMPLATE_NODE_IF)
state = TEMPLATE_BLOCK_IF_OPERATOR_START;
@@ -417,22 +417,21 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
break;
case TEMPLATE_BLOCK_IF_OPERATOR_START:
- if (c == ' ') {
+ if (bc_isspace(c))
break;
- }
state = TEMPLATE_BLOCK_IF_OPERATOR;
op_start = current;
break;
case TEMPLATE_BLOCK_IF_OPERATOR:
- if (c != ' ')
+ if (!bc_isspace(c))
break;
state = TEMPLATE_BLOCK_IF_OPERAND_START;
op_end = current;
break;
case TEMPLATE_BLOCK_IF_OPERAND_START:
- if (c == ' ')
+ if (bc_isspace(c))
break;
if (c >= 'A' && c <= 'Z') {
state = TEMPLATE_BLOCK_IF_VARIABLE_OPERAND;
@@ -469,7 +468,7 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
break;
case TEMPLATE_BLOCK_FOREACH_START:
- if (c == ' ')
+ if (bc_isspace(c))
break;
if (c >= 'A' && c <= 'Z') {
state = TEMPLATE_BLOCK_FOREACH_VARIABLE;
@@ -485,7 +484,7 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
case TEMPLATE_BLOCK_FOREACH_VARIABLE:
if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')
break;
- if (c == ' ') {
+ if (bc_isspace(c)) {
end = current;
state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER;
break;
@@ -497,7 +496,7 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
break;
case TEMPLATE_BLOCK_END_WHITESPACE_CLEANER:
- if (c == ' ')
+ if (bc_isspace(c))
break;
if (c == '-') {
lstrip_next = true;
@@ -524,7 +523,7 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
break;
case TEMPLATE_VARIABLE_START:
- if (c == ' ')
+ if (bc_isspace(c))
break;
if (c >= 'A' && c <= 'Z') {
state = TEMPLATE_VARIABLE;
@@ -540,7 +539,7 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
case TEMPLATE_VARIABLE:
if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')
break;
- if (c == ' ') {
+ if (bc_isspace(c)) {
end = current;
state = TEMPLATE_VARIABLE_END;
break;
@@ -557,7 +556,7 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
break;
case TEMPLATE_VARIABLE_END:
- if (c == ' ')
+ if (bc_isspace(c))
break;
if (c == '}') {
state = TEMPLATE_CLOSE_BRACKET;
diff --git a/src/common/utils.c b/src/common/utils.c
index 530ab6c..692d1ce 100644
--- a/src/common/utils.c
+++ b/src/common/utils.c
@@ -167,6 +167,14 @@ bc_strdup_printf(const char *format, ...)
}
+// locale-independent implementation of isspace
+bool
+bc_isspace(int c)
+{
+ return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v';
+}
+
+
bool
bc_str_starts_with(const char *str, const char *prefix)
{
diff --git a/src/common/utils.h b/src/common/utils.h
index bc3a157..94c3356 100644
--- a/src/common/utils.h
+++ b/src/common/utils.h
@@ -42,6 +42,7 @@ char* bc_strdup(const char *s);
char* bc_strndup(const char *s, size_t n);
char* bc_strdup_vprintf(const char *format, va_list ap);
char* bc_strdup_printf(const char *format, ...);
+bool bc_isspace(int c);
bool bc_str_starts_with(const char *str, const char *prefix);
bool bc_str_ends_with(const char *str, const char *suffix);
char* bc_str_lstrip(char *str);
diff --git a/tests/blogc/check_template_parser.c b/tests/blogc/check_template_parser.c
index bf9f6d7..0121f73 100644
--- a/tests/blogc/check_template_parser.c
+++ b/tests/blogc/check_template_parser.c
@@ -339,6 +339,135 @@ test_template_parse_html(void **state)
static void
+test_template_parse_html_whitespace(void **state)
+{
+ const char *a =
+ "<html>\n"
+ " <head>\n"
+ " {%\n block entry\n%}\n"
+ " <title>My cool blog >> {{ TITLE }}</title>\n"
+ " {% \vendblock\v %}\n"
+ " {% \r\nblock listing_once\n%}\n"
+ " <title>My cool blog - Main page</title>\n"
+ " {%\t endblock\t%}\n"
+ " </head>\n"
+ " <body>\n"
+ " <h1>My cool blog</h1>\n"
+ " {%\t\t\tblock entry\n%}\n"
+ " <h2>{{\tTITLE\n}}</h2>\n"
+ " {%\nifdef DATE\v%}<h4>Published in: {{\tDATE\t}}</h4>{%\fendif\f%}\n"
+ " <pre>{{\tCONTENT\n}}</pre>\n"
+ " {%\n\n \nendblock\f\f\n\t%}\n"
+ " {%\nblock\n\nlisting_once\t%}<ul>{%\tendblock\f\f%}\n"
+ " {%\n\nblock\t\tlisting\f\t%}<p><a href=\"{{\t\fFILENAME\t\t}}.html\">"
+ "{{ TITLE }}</a>{%\f\tifdef\v\vDATE\f%} - {{\fDATE\f}}{%\tendif\f%}</p>{%\tendblock\t%}\n"
+ " {%\tblock\tlisting_once\t%}</ul>{%\nendblock\n%}\n"
+ " </body>\n"
+ "</html>\n";
+ bc_error_t *err = NULL;
+ bc_slist_t *ast = blogc_template_parse(a, strlen(a), &err);
+ assert_null(err);
+ assert_non_null(ast);
+ blogc_assert_template_node(ast, "<html>\n <head>\n ",
+ BLOGC_TEMPLATE_NODE_CONTENT);
+ blogc_assert_template_node(ast->next, "entry",
+ BLOGC_TEMPLATE_NODE_BLOCK);
+ blogc_assert_template_node(ast->next->next,
+ "\n <title>My cool blog >> ", BLOGC_TEMPLATE_NODE_CONTENT);
+ blogc_assert_template_node(ast->next->next->next, "TITLE",
+ BLOGC_TEMPLATE_NODE_VARIABLE);
+ blogc_assert_template_node(ast->next->next->next->next,
+ "</title>\n ", BLOGC_TEMPLATE_NODE_CONTENT);
+ blogc_assert_template_node(ast->next->next->next->next->next, NULL,
+ BLOGC_TEMPLATE_NODE_ENDBLOCK);
+ blogc_assert_template_node(ast->next->next->next->next->next->next,
+ "\n ", BLOGC_TEMPLATE_NODE_CONTENT);
+ blogc_assert_template_node(ast->next->next->next->next->next->next->next,
+ "listing_once", BLOGC_TEMPLATE_NODE_BLOCK);
+ bc_slist_t *tmp = ast->next->next->next->next->next->next->next->next;
+ blogc_assert_template_node(tmp,
+ "\n <title>My cool blog - Main page</title>\n ",
+ BLOGC_TEMPLATE_NODE_CONTENT);
+ blogc_assert_template_node(tmp->next, NULL, BLOGC_TEMPLATE_NODE_ENDBLOCK);
+ blogc_assert_template_node(tmp->next->next,
+ "\n </head>\n <body>\n <h1>My cool blog</h1>\n ",
+ BLOGC_TEMPLATE_NODE_CONTENT);
+ blogc_assert_template_node(tmp->next->next->next, "entry",
+ BLOGC_TEMPLATE_NODE_BLOCK);
+ blogc_assert_template_node(tmp->next->next->next->next,
+ "\n <h2>", BLOGC_TEMPLATE_NODE_CONTENT);
+ blogc_assert_template_node(tmp->next->next->next->next->next,
+ "TITLE", BLOGC_TEMPLATE_NODE_VARIABLE);
+ blogc_assert_template_node(tmp->next->next->next->next->next->next,
+ "</h2>\n ", BLOGC_TEMPLATE_NODE_CONTENT);
+ blogc_assert_template_node(tmp->next->next->next->next->next->next->next,
+ "DATE", BLOGC_TEMPLATE_NODE_IFDEF);
+ tmp = tmp->next->next->next->next->next->next->next->next;
+ blogc_assert_template_node(tmp, "<h4>Published in: ",
+ BLOGC_TEMPLATE_NODE_CONTENT);
+ blogc_assert_template_node(tmp->next, "DATE", BLOGC_TEMPLATE_NODE_VARIABLE);
+ blogc_assert_template_node(tmp->next->next, "</h4>",
+ BLOGC_TEMPLATE_NODE_CONTENT);
+ blogc_assert_template_node(tmp->next->next->next, NULL,
+ BLOGC_TEMPLATE_NODE_ENDIF);
+ blogc_assert_template_node(tmp->next->next->next->next, "\n <pre>",
+ BLOGC_TEMPLATE_NODE_CONTENT);
+ blogc_assert_template_node(tmp->next->next->next->next->next,
+ "CONTENT", BLOGC_TEMPLATE_NODE_VARIABLE);
+ blogc_assert_template_node(tmp->next->next->next->next->next->next,
+ "</pre>\n ", BLOGC_TEMPLATE_NODE_CONTENT);
+ blogc_assert_template_node(tmp->next->next->next->next->next->next->next,
+ NULL, BLOGC_TEMPLATE_NODE_ENDBLOCK);
+ tmp = tmp->next->next->next->next->next->next->next->next;
+ blogc_assert_template_node(tmp, "\n ", BLOGC_TEMPLATE_NODE_CONTENT);
+ blogc_assert_template_node(tmp->next, "listing_once",
+ BLOGC_TEMPLATE_NODE_BLOCK);
+ blogc_assert_template_node(tmp->next->next, "<ul>",
+ BLOGC_TEMPLATE_NODE_CONTENT);
+ blogc_assert_template_node(tmp->next->next->next, NULL,
+ BLOGC_TEMPLATE_NODE_ENDBLOCK);
+ blogc_assert_template_node(tmp->next->next->next->next, "\n ",
+ BLOGC_TEMPLATE_NODE_CONTENT);
+ blogc_assert_template_node(tmp->next->next->next->next->next,
+ "listing", BLOGC_TEMPLATE_NODE_BLOCK);
+ blogc_assert_template_node(tmp->next->next->next->next->next->next,
+ "<p><a href=\"", BLOGC_TEMPLATE_NODE_CONTENT);
+ blogc_assert_template_node(tmp->next->next->next->next->next->next->next,
+ "FILENAME", BLOGC_TEMPLATE_NODE_VARIABLE);
+ tmp = tmp->next->next->next->next->next->next->next->next;
+ blogc_assert_template_node(tmp, ".html\">", BLOGC_TEMPLATE_NODE_CONTENT);
+ blogc_assert_template_node(tmp->next, "TITLE",
+ BLOGC_TEMPLATE_NODE_VARIABLE);
+ blogc_assert_template_node(tmp->next->next, "</a>",
+ BLOGC_TEMPLATE_NODE_CONTENT);
+ blogc_assert_template_node(tmp->next->next->next, "DATE",
+ BLOGC_TEMPLATE_NODE_IFDEF);
+ blogc_assert_template_node(tmp->next->next->next->next, " - ",
+ BLOGC_TEMPLATE_NODE_CONTENT);
+ blogc_assert_template_node(tmp->next->next->next->next->next, "DATE",
+ BLOGC_TEMPLATE_NODE_VARIABLE);
+ blogc_assert_template_node(tmp->next->next->next->next->next->next,
+ NULL, BLOGC_TEMPLATE_NODE_ENDIF);
+ blogc_assert_template_node(tmp->next->next->next->next->next->next->next,
+ "</p>", BLOGC_TEMPLATE_NODE_CONTENT);
+ tmp = tmp->next->next->next->next->next->next->next->next;
+ blogc_assert_template_node(tmp, NULL, BLOGC_TEMPLATE_NODE_ENDBLOCK);
+ blogc_assert_template_node(tmp->next, "\n ",
+ BLOGC_TEMPLATE_NODE_CONTENT);
+ blogc_assert_template_node(tmp->next->next, "listing_once",
+ BLOGC_TEMPLATE_NODE_BLOCK);
+ blogc_assert_template_node(tmp->next->next->next, "</ul>",
+ BLOGC_TEMPLATE_NODE_CONTENT);
+ blogc_assert_template_node(tmp->next->next->next->next, NULL,
+ BLOGC_TEMPLATE_NODE_ENDBLOCK);
+ blogc_assert_template_node(tmp->next->next->next->next->next,
+ "\n </body>\n</html>\n", BLOGC_TEMPLATE_NODE_CONTENT);
+ assert_null(tmp->next->next->next->next->next->next);
+ blogc_template_free_ast(ast);
+}
+
+
+static void
test_template_parse_ifdef_and_var_outside_block(void **state)
{
const char *a =
@@ -1137,6 +1266,7 @@ main(void)
unit_test(test_template_parse),
unit_test(test_template_parse_crlf),
unit_test(test_template_parse_html),
+ unit_test(test_template_parse_html_whitespace),
unit_test(test_template_parse_ifdef_and_var_outside_block),
unit_test(test_template_parse_nested_else),
unit_test(test_template_parse_invalid_block_start),