summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinnnus <[email protected]>2024-03-07 21:30:29 +0100
committerLinnnus <[email protected]>2024-03-07 21:30:29 +0100
commit7af40dac48281448e000e65918d7aec6bd1aa8dc (patch)
tree8e25a6b3a574ad371a2d308916e006af2ea7d65d
parent89be2e2cc472b8b6303e7a3f4350467edd4f70ad (diff)
feat(creole): Add nowiki blocks
-rw-r--r--src/creole.c32
-rw-r--r--src/creole_test_main.c23
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("&amp;", 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, // <p> 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("<pre><code>", out);
+ hprint(out, start, stop);
+ fputs("</code></pre>", 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 = "<p>Creole: Inline nowiki with closing braces: <tt>if (a&gt;b) { b = a; }</tt>.</p>"
},
+ {
+ .name = "Nowiki block",
+ .input = "Here is some stuff:\n\n{{{\nwad\n}}}",
+ .output = "<p>Here is some stuff:</p>"
+ "<pre><code>wad</code></pre>"
+ },
+ {
+ .name = "Non-closed nowiki block",
+ .input = "Here is some stuff:\n\n{{{\nwad",
+ .output = "<p>Here is some stuff:</p>"
+ "<p>{{{\nwad</p>"
+ },
+ {
+ .name = "Empty nowiki block",
+ .input = "{{{\n}}}",
+ .output = "<pre><code></code></pre>"
+ },
+ { // 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 = "<pre><code>if (x != NULL) {\n for (i = 0; i &lt; size; i++) {\n if (x[i] &gt; 0) {\n x[i]--;\n }}}</code></pre>",
+ },
#if 0
{
.name = "Simple unordered list",