From 7af40dac48281448e000e65918d7aec6bd1aa8dc Mon Sep 17 00:00:00 2001 From: Linnnus Date: Thu, 7 Mar 2024 21:30:29 +0100 Subject: feat(creole): Add nowiki blocks --- src/creole.c | 32 +++++++++++++++++++++++++++++--- src/creole_test_main.c | 23 +++++++++++++++++++++++ 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/creole.c b/src/creole.c index 56edbd8..2a16205 100644 --- a/src/creole.c +++ b/src/creole.c @@ -21,10 +21,15 @@ long do_raw_url(const char *begin, const char *end, bool new_block, FILE *out); long do_emphasis(const char *begin, const char *end, bool new_block, FILE *out); long do_bold(const char *begin, const char *end, bool new_block, FILE *out); long do_nowiki_inline(const char *begin, const char *end, bool new_block, FILE *out); +long do_nowiki_block(const char *begin, const char *end, bool new_block, FILE *out); -// Prints string escaped. +// Prints string with special HTML characters escaped. +// +// Unlike many other functions, this function does not assume that (end >= +// begin). This simplifies some logic in callers since bracket-matching is prone +// to off-by-one errors when the brackets are empty. void hprint(FILE *out, const char *begin, const char *end) { - for (const char *p = begin; p != end; p++) { + for (const char *p = begin; p < end; p++) { if (*p == '&') { fputs("&", out); } else if (*p == '"') { @@ -59,6 +64,7 @@ typedef long (* parser_t)(const char *begin, const char *end, bool new_block, FI static parser_t parsers[] = { // Block-level elements do_headers, + do_nowiki_block, do_paragraph, //

should be last as it eats anything // Inline-level elements @@ -356,7 +362,7 @@ long do_nowiki_inline(const char *begin, const char *end, bool new_block, FILE * trim_start += 1; } const char *trim_stop = stop; - while (isspace(trim_stop[-1])) { + while (isspace(trim_stop[-1]) && trim_start <= trim_stop - 1) { trim_stop -= 1; } @@ -367,7 +373,27 @@ long do_nowiki_inline(const char *begin, const char *end, bool new_block, FILE * return 3 + (stop - start) + 3; /* {{{...}}} */ } +long do_nowiki_block(const char *begin, const char *end, bool new_block, FILE *out) { + if (!(new_block && starts_with(begin, end, "{{{\n"))) { + return 0; + } + const char *start = begin + 4; + + const char *stop = strnstr(start - 1, "\n}}}", end - (start - 1)); + if (stop == NULL) { + return 0; + } + + fputs("

", out);
+	hprint(out, start, stop);
+	fputs("
", out); + + return -(stop - start + 8); +} + void process(const char *begin, const char *end, bool new_block, FILE *out) { + assert(begin <= end); + const char *p = begin; while (p < end) { // Eat all newlines if we're starting a block. diff --git a/src/creole_test_main.c b/src/creole_test_main.c index 8d531c7..4f71087 100644 --- a/src/creole_test_main.c +++ b/src/creole_test_main.c @@ -223,6 +223,29 @@ struct { .input = "Creole: Inline nowiki with closing braces: {{{if (a>b) { b = a; }}}}.", .output = "

Creole: Inline nowiki with closing braces: if (a>b) { b = a; }.

" }, + { + .name = "Nowiki block", + .input = "Here is some stuff:\n\n{{{\nwad\n}}}", + .output = "

Here is some stuff:

" + "
wad
" + }, + { + .name = "Non-closed nowiki block", + .input = "Here is some stuff:\n\n{{{\nwad", + .output = "

Here is some stuff:

" + "

{{{\nwad

" + }, + { + .name = "Empty nowiki block", + .input = "{{{\n}}}", + .output = "
" + }, + { // Spec: In preformatted blocks, since markers must not be preceded by leading spaces, lines with three closing braces + // which belong to the preformatted block must follow at least one space. In the rendered output, one leading space is removed. + .name = "", + .input = "{{{\nif (x != NULL) {\n for (i = 0; i < size; i++) {\n if (x[i] > 0) {\n x[i]--;\n }}}\n}}}\n", + .output = "
if (x != NULL) {\n  for (i = 0; i < size; i++) {\n    if (x[i] > 0) {\n      x[i]--;\n  }}}
", + }, #if 0 { .name = "Simple unordered list", -- cgit v1.2.3