author | Olivier Brunel
<jjk@jjacky.com> 2022-12-24 22:23:42 UTC |
committer | Olivier Brunel
<jjk@jjacky.com> 2022-12-24 22:53:06 UTC |
parent | a9994fcc41faed6d21c952481b2d6115905e6cbe |
base.md | +1 | -1 |
main.c | +178 | -178 |
diff --git a/base.md b/base.md index 541d5fc..69e6bbf 100644 --- a/base.md +++ b/base.md @@ -88,7 +88,7 @@ in ``pre``/``code`` blocks too (unlike the others) : Oh yeah, we might wanna have "code blocks". Those would be like ``pre`` ones, only a bit more styled : -```c +```c hl #include <stdlib.h> int main(int argc, char **argv) { diff --git a/main.c b/main.c index 8e1fd3d..886bdb1 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,6 @@ #define _GNU_SOURCE #include <stdio.h> +#include <stdarg.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> @@ -16,6 +17,7 @@ enum { ERR_MEM = -7, ERR_NOTSUPP = -8, ERR_INVALID = -9, + ERR_WRITE = -10, }; enum { @@ -25,6 +27,7 @@ enum { }; struct ctx { + FILE *out; struct { int flags; int from; @@ -36,53 +39,148 @@ struct ctx { } buf; }; -static int highlight_code_text(const char *text, size_t size, void *ctx_); -static int escape_text(FILE *f, const char *text, size_t size); +static int raw_text(FILE *f, const char *text, size_t size) +{ + if (fwrite(text, 1, size, f) != size) return ERR_WRITE; + else return 0; +} + +#define raw_str(f,s) raw_text(f, s, strlen(s)) +#define raw_or_ret(f,s) if (raw_str(f, s) < 0) return ERR_WRITE + +static int raw_printf(FILE *f, const char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + int r = vfprintf(f, fmt, va); + va_end(va); + return (r < 0) ? ERR_WRITE : 0; +} + +static int escape_text(FILE *f, const char *text, size_t size) +{ + size_t last = 0; + for (size_t n = 0; n < size; ++n) { + const char *esc = NULL; + switch (text[n]) { + case '&': + if (!esc) esc = "&"; + /* fall through */ + case '<': + if (!esc) esc = "<"; + /* fall through */ + case '>': + if (!esc) esc = ">"; + /* fall through */ + case '"': + if (!esc) esc = """; + if (raw_text(f, text + last, n - last) < 0 || raw_str(f, esc) < 0) + return ERR_WRITE; + last = n + 1; + break; + } + } + if (raw_text(f, text + last, size - last) < 0) + return ERR_WRITE; + + return 0; +} + +static int highlight_escape_text(FILE *f, const char *text, size_t size) +{ + const char *end = text + size; + + for ( ; text < end; ) { + const char *open, *close; + open = memmem(text, size, "<hl>", 4); + if (!open) break; + close = memmem(open + 4, end - open - 4, "</hl>", 5); + if (!close) break; + if (escape_text(f, text, open - text) < 0 + || raw_str(f, "<span class=\"highlighted\">") < 0 + || escape_text(f, open + 4, close - open - 4) < 0 + || raw_str(f, "</span>") < 0) + return ERR_WRITE; + text = close + 5; + size = end - text; + } + if (escape_text(f, text, size) < 0) + return ERR_WRITE ; + + return 0; +} + +static int attribute(FILE *f, MD_ATTRIBUTE *attr) +{ + int n = 0; + MD_SIZE l, cur = 0, size = attr->size; + + while (cur < size) { + l = attr->substr_offsets[n + 1] - attr->substr_offsets[n]; + switch (attr->substr_types[n]) { + case MD_TEXT_NORMAL: + case MD_TEXT_ENTITY: + if (escape_text(f, attr->text, l) < 0) + return ERR_WRITE; + break; + + case MD_TEXT_NULLCHAR: + break; + + case MD_TEXT_BR: + case MD_TEXT_SOFTBR: + case MD_TEXT_CODE: + case MD_TEXT_HTML: + case MD_TEXT_LATEXMATH: + /* actually not possible, but this will silence some warnings */ + return ERR_NOTSUPP; + } + cur += attr->substr_offsets[++n]; + } + return 0; +} + static int enter_block(MD_BLOCKTYPE type, void *details, void *ctx_) { + struct ctx *ctx = ctx_; + FILE *f = ctx->out; + switch (type) { case MD_BLOCK_DOC: - printf("<html><body>"); - break; + return raw_str(f, "<html><body>"); case MD_BLOCK_QUOTE: - printf("<blockquote>"); - break; + return raw_str(f, "<blockquote>"); case MD_BLOCK_UL: - printf("<ul>"); - break; + return raw_str(f, "<ul>"); case MD_BLOCK_OL: - printf("<ol>"); - break; + return raw_str(f, "<ol>"); case MD_BLOCK_LI: - printf("<li>"); - break; + return raw_str(f, "<li>"); case MD_BLOCK_HR: - printf("<hr>"); - break; + return raw_str(f, "<hr>"); case MD_BLOCK_H: { MD_BLOCK_H_DETAIL *d = details; - printf("<h%d>", d->level); + return raw_printf(f, "<h%d>", d->level); } break; case MD_BLOCK_CODE: { - struct ctx *ctx = ctx_; MD_BLOCK_CODE_DETAIL *d = details; if (ctx->code.flags) return ERR_INVALID; if (!d->info.text || !strncmp(d->info.text, "pre\n", 4)) { - printf("<pre>"); + return raw_str(f, "<pre>"); } else { const char *t = d->info.text; size_t l = d->info.size; @@ -126,7 +224,7 @@ static int enter_block(MD_BLOCKTYPE type, void *details, void *ctx_) } if (!(ctx->code.flags & CODE_BUFFERED)) - printf("<pre>"); + return raw_str(f, "<pre>"); } } break; @@ -135,8 +233,7 @@ static int enter_block(MD_BLOCKTYPE type, void *details, void *ctx_) return ERR_NOTSUPP; case MD_BLOCK_P: - printf("<p>"); - break; + return raw_str(f, "<p>"); case MD_BLOCK_TABLE: case MD_BLOCK_THEAD: @@ -152,26 +249,24 @@ static int enter_block(MD_BLOCKTYPE type, void *details, void *ctx_) static int leave_block(MD_BLOCKTYPE type, void *details, void *ctx_) { + struct ctx *ctx = ctx_; + FILE *f = ctx->out; + switch (type) { case MD_BLOCK_DOC: - printf("</body></html>"); - break; + return raw_str(f, "</body></html>"); case MD_BLOCK_QUOTE: - printf("</blockquote>"); - break; + return raw_str(f, "</blockquote>"); case MD_BLOCK_UL: - printf("</ul>"); - break; + return raw_str(f, "</ul>"); case MD_BLOCK_OL: - printf("</ol>"); - break; + return raw_str(f, "</ol>"); case MD_BLOCK_LI: - printf("</li>"); - break; + return raw_str(f, "</li>"); case MD_BLOCK_HR: break; @@ -179,7 +274,7 @@ static int leave_block(MD_BLOCKTYPE type, void *details, void *ctx_) case MD_BLOCK_H: { MD_BLOCK_H_DETAIL *d = details; - printf("</h%d>", d->level); + return raw_printf(f, "</h%d>", d->level); } break; @@ -188,7 +283,7 @@ static int leave_block(MD_BLOCKTYPE type, void *details, void *ctx_) struct ctx *ctx = ctx_; if (!(ctx->code.flags & CODE_BUFFERED)) { - printf("</pre>"); + return raw_str(f, "</pre>"); } else { if (fclose(ctx->buf.f)) return ERR_MEM; ctx->buf.f = NULL; @@ -197,31 +292,35 @@ static int leave_block(MD_BLOCKTYPE type, void *details, void *ctx_) const char *s = ctx->buf.str; size_t l = ctx->buf.len; - printf("<pre class=\"lineno\">"); + raw_or_ret(f, "<pre class=\"lineno\">"); for (int n = ctx->code.from; s; ++n) { const char *e = memchr(s, '\n', l); if (!e) break; l -= ++e - s; s = e; - printf("%d\n", n); + if (raw_printf(f, "%d\n", n) < 0) + return ERR_WRITE; } - printf("</pre><code>"); + raw_or_ret(f, "</pre><code>"); } else { - printf("<pre>"); + raw_or_ret(f, "<pre>"); } - int flags = ctx->code.flags; - ctx->code.flags = 0; - + int r; if (ctx->code.flags & CODE_HIGHLIGHT) - highlight_code_text(ctx->buf.str, ctx->buf.len, ctx); + r = highlight_escape_text(f, ctx->buf.str, ctx->buf.len); else - fwrite(ctx->buf.str, 1, ctx->buf.len, stdout); + r = escape_text(f, ctx->buf.str, ctx->buf.len); + if (r < 0) return ERR_WRITE; - if (flags & CODE_LINES) - printf("</code>"); + if (ctx->code.flags & CODE_LINES) + r = raw_str(f, "</code>"); else - printf("</pre>"); + r = raw_str(f, "</pre>"); + if (r < 0) return ERR_WRITE; + + free(ctx->buf.str); + ctx->code.flags = 0; } } break; @@ -230,8 +329,7 @@ static int leave_block(MD_BLOCKTYPE type, void *details, void *ctx_) return ERR_NOTSUPP; case MD_BLOCK_P: - printf("</p>"); - break; + return raw_str(f, "</p>"); case MD_BLOCK_TABLE: case MD_BLOCK_THEAD: @@ -245,78 +343,45 @@ static int leave_block(MD_BLOCKTYPE type, void *details, void *ctx_) return 0; } -static int attribute(MD_ATTRIBUTE *attr, void *ctx_) -{ - int n = 0; - MD_SIZE l, cur = 0, size = attr->size; - - while (cur < size) { - l = attr->substr_offsets[n + 1] - attr->substr_offsets[n]; - switch (attr->substr_types[n]) { - case MD_TEXT_NORMAL: - case MD_TEXT_ENTITY: - escape_text(stdout, attr->text, l); - break; - - case MD_TEXT_NULLCHAR: - break; - - case MD_TEXT_BR: - case MD_TEXT_SOFTBR: - case MD_TEXT_CODE: - case MD_TEXT_HTML: - case MD_TEXT_LATEXMATH: - /* actually not possible, but this will silence some warnings */ - return ERR_NOTSUPP; - } - cur += attr->substr_offsets[++n]; - } - return 0; - -} - static int enter_span(MD_SPANTYPE type, void *details, void *ctx_) { + struct ctx *ctx = ctx_; + FILE *f = ctx->out; int r; switch (type) { case MD_SPAN_EM: - printf("<em>"); - break; + return raw_str(f, "<em>"); case MD_SPAN_STRONG: - printf("<strong>"); - break; + return raw_str(f, "<strong>"); case MD_SPAN_A: { MD_SPAN_A_DETAIL *d = details; - printf("<a href=\""); - r = attribute(&d->href, ctx_); + raw_or_ret(f, "<a href=\""); + r = attribute(f, &d->href); if (r < 0) return r; if (d->title.text) { - printf("\" title=\""); - r = attribute(&d->title, ctx_); + raw_or_ret(f, "\" title=\""); + r = attribute(f, &d->title); if (r < 0) return r; } - printf("\">"); + return raw_str(f, "\">"); } - break; case MD_SPAN_IMG: /* TODO */ break; case MD_SPAN_CODE: - printf("<tt>"); - break; + return raw_str(f, "<tt>"); case MD_SPAN_DEL: - printf("<s>"); - break; + return raw_str(f, "<s>"); case MD_SPAN_LATEXMATH: case MD_SPAN_LATEXMATH_DISPLAY: @@ -324,9 +389,7 @@ static int enter_span(MD_SPANTYPE type, void *details, void *ctx_) return ERR_NOTSUPP; case MD_SPAN_U: - printf("<u>"); - break; - + return raw_str(f, "<u>"); } return 0; @@ -334,30 +397,28 @@ static int enter_span(MD_SPANTYPE type, void *details, void *ctx_) static int leave_span(MD_SPANTYPE type, void *details, void *ctx_) { + struct ctx *ctx = ctx_; + FILE *f = ctx->out; + switch (type) { case MD_SPAN_EM: - printf("</em>"); - break; + return raw_str(f, "</em>"); case MD_SPAN_STRONG: - printf("</strong>"); - break; + return raw_str(f, "</strong>"); case MD_SPAN_A: - printf("</a>"); - break; + return raw_str(f, "</a>"); case MD_SPAN_IMG: /* TODO */ break; case MD_SPAN_CODE: - printf("</tt>"); - break; + return raw_str(f, "</tt>"); case MD_SPAN_DEL: - printf("</s>"); - break; + return raw_str(f, "</s>"); case MD_SPAN_LATEXMATH: case MD_SPAN_LATEXMATH_DISPLAY: @@ -365,104 +426,46 @@ static int leave_span(MD_SPANTYPE type, void *details, void *ctx_) return ERR_NOTSUPP; case MD_SPAN_U: - printf("</u>"); - break; - - } - - return 0; -} - -static int escape_text(FILE *f, const char *text, size_t size) -{ - size_t last = 0; - for (size_t n = 0; n < size; ++n) { - const char *esc = NULL; - switch (text[n]) { - case '&': - if (!esc) esc = "&"; - /* fall through */ - case '<': - if (!esc) esc = "<"; - /* fall through */ - case '>': - if (!esc) esc = ">"; - /* fall through */ - case '"': - if (!esc) esc = """; - fprintf(f, "%.*s%s", (int) (n - last), text + last, esc); - last = n + 1; - break; - } + return raw_str(f, "</u>"); } - fprintf(f, "%.*s", (int) (size - last), text + last); return 0; } -static int highlight_code_text(const char *text, size_t size, void *ctx_) +static int text(MD_TEXTTYPE type, const MD_CHAR *text, MD_SIZE size, void *ctx_) { struct ctx *ctx = ctx_; - FILE *f = (ctx->code.flags & CODE_BUFFERED) ? ctx->buf.f : stdout; - const char *end = text + size; - - for ( ; text < end; ) { - const char *open, *close; - open = memmem(text, size, "<hl>", 4); - if (!open) break; - close = memmem(open + 4, end - open - 4, "</hl>", 5); - if (!close) break; - fprintf(f, "%.*s%s%.*s%s", - (int) (open - text), text, - "<span class=\"highlighted\">", - (int) (close - open - 4), open + 4, - "</span>"); - text = close + 5; - size = end - text; - } - fprintf(f, "%.*s", (int) size, text); - return 0; -} + FILE *f = ctx->out; -static int text(MD_TEXTTYPE type, const MD_CHAR *text, MD_SIZE size, void *ctx_) -{ switch (type) { case MD_TEXT_NORMAL: - return escape_text(stdout, text, size); + return escape_text(f, text, size); case MD_TEXT_NULLCHAR: break; case MD_TEXT_BR: - printf("<br>"); - break; + return raw_str(f, "<br>"); case MD_TEXT_SOFTBR: - printf("\n"); - break; + return raw_str(f, "\n"); case MD_TEXT_ENTITY: - escape_text(stdout, text, size); - break; + return escape_text(stdout, text, size); case MD_TEXT_CODE: { struct ctx *ctx = ctx_; - if (!(ctx->code.flags & CODE_BUFFERED)) { - escape_text(stdout, text, size); - break; - } + if (!(ctx->code.flags & CODE_BUFFERED)) + return escape_text(f, text, size); if (!ctx->buf.f) { ctx->buf.f = open_memstream(&ctx->buf.str, &ctx->buf.len); if (!ctx->buf.f) return ERR_MEM; } - if (ctx->code.flags & CODE_HIGHLIGHT) - return highlight_code_text(text, size, ctx_); - else - return escape_text(ctx->buf.f, text, size); + return raw_text(ctx->buf.f, text, size); } case MD_TEXT_HTML: @@ -488,15 +491,12 @@ static int text(MD_TEXTTYPE type, const MD_CHAR *text, MD_SIZE size, void *ctx_) int i, n; for (i = 0, n = sizeof(tags) / sizeof(*tags); i < n; ++i) { if (size == tags[i].len && !strncmp(text, tags[i].name, tags[i].len)) { - if (tags[i].repl) - printf("%s", tags[i].repl); - else - printf("%.*s", (int) tags[i].len, tags[i].name); + raw_or_ret(f, (tags[i].repl) ? tags[i].repl : tags[i].name); break; } } - if (i == n) - escape_text(stdout, text, size); + if (i == n && escape_text(f, text, size) < 0) + return ERR_WRITE; break; case MD_TEXT_LATEXMATH: @@ -554,7 +554,7 @@ int main (int argc, char *argv[]) .leave_span = leave_span, .text = text, }; - struct ctx ctx = { 0 }; + struct ctx ctx = { .out = stdout }; int r = md_parse(buf, done, &parser, &ctx); if (r < 0) errx(-ERR_PARSER, "parser internal error %d", -r); else if (r > 0) errx(-r, "parser error %d", r);