author | Olivier Brunel
<jjk@jjacky.com> 2022-12-26 23:06:07 UTC |
committer | Olivier Brunel
<jjk@jjacky.com> 2022-12-26 23:08:37 UTC |
parent | 0fb7e1d14b2a949721de1256a0e074fb38db6a89 |
main.c | +205 | -145 |
diff --git a/main.c b/main.c index dc5ff98..fb8b7ab 100644 --- a/main.c +++ b/main.c @@ -20,9 +20,6 @@ const char *PROG = "qmdoc"; -#define ret(r,...) { warn(__VA_ARGS__); return r; } -#define retx(r,...) { warnx(__VA_ARGS__); return r; } - enum { OPT_NO_CSS = (1 << 0), OPT_INLINE_CSS = (1 << 1), @@ -58,9 +55,15 @@ struct page { size_t size; }; +enum { + BUF_OFF = 0, + BUF_WAITING, + BUF_ON +}; + struct ctx { int options; - FILE *out; + buffer out; stralloc sa_names; struct css *css; struct page *pages; @@ -71,34 +74,28 @@ struct ctx { int from; } code; struct { - char *str; - size_t len; - FILE *f; + size_t salen; + int state; } buf; }; static int -raw_text(FILE *f, const char *text, size_t size) +raw_text(struct ctx *ctx, const char *text, size_t size) { - if (fwrite(text, 1, size, f) != size) return ERR_IO; - else return 0; + if (ctx->buf.state == BUF_OFF) { + if (buffer_put(&ctx->out, text, size) != size) return 0; + else return 1; + } else if (ctx->buf.state == BUF_WAITING) { + ctx->buf.salen = ctx->sa_names.len; + ctx->buf.state = BUF_ON; + } + return stralloc_catb(&ctx->sa_names, text, size); } -#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_IO - -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_IO : 0; -} +#define raw_str(ctx,s) raw_text(ctx, s, strlen(s)) static int -escape_text(FILE *f, const char *text, size_t size) +escape_text(struct ctx *ctx, const char *text, size_t size) { size_t last = 0; for (size_t n = 0; n < size; ++n) { @@ -115,20 +112,20 @@ escape_text(FILE *f, const char *text, size_t size) /* fall through */ case '"': if (!esc) esc = """; - if (raw_text(f, text + last, n - last) < 0 || raw_str(f, esc) < 0) - return ERR_IO; + if (!raw_text(ctx, text + last, n - last) || !raw_str(ctx, esc)) + return 0; last = n + 1; break; } } - if (raw_text(f, text + last, size - last) < 0) - return ERR_IO; + if (!raw_text(ctx, text + last, size - last)) + return 0; - return 0; + return 1; } static int -highlight_escape_text(FILE *f, const char *text, size_t size) +highlight_escape_text(struct ctx *ctx, const char *text, size_t size) { const char *end = text + size; @@ -138,22 +135,22 @@ highlight_escape_text(FILE *f, const char *text, size_t size) 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_IO; + if (!escape_text(ctx, text, open - text) + || !raw_str(ctx, "<span class=\"highlighted\">") + || !escape_text(ctx, open + 4, close - open - 4) + || !raw_str(ctx, "</span>")) + return 0; text = close + 5; size = end - text; } - if (escape_text(f, text, size) < 0) - return ERR_IO ; + if (!escape_text(ctx, text, size)) + return 0 ; - return 0; + return 1; } static int -attribute(FILE *f, MD_ATTRIBUTE *attr) +attribute(struct ctx *ctx, MD_ATTRIBUTE *attr) { int n = 0; MD_SIZE l, cur = 0, size = attr->size; @@ -163,8 +160,8 @@ attribute(FILE *f, MD_ATTRIBUTE *attr) switch (attr->substr_types[n]) { case MD_TEXT_NORMAL: case MD_TEXT_ENTITY: - if (escape_text(f, attr->text, l) < 0) - return ERR_IO; + if (!escape_text(ctx, attr->text, l)) + return 0; break; case MD_TEXT_NULLCHAR: @@ -180,7 +177,7 @@ attribute(FILE *f, MD_ATTRIBUTE *attr) } cur += attr->substr_offsets[++n]; } - return 0; + return 1; } @@ -188,67 +185,84 @@ 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: { - int r = raw_str(f, "<!DOCTYPE html>\n<html><head>"); - if (r < 0) return r; - r = raw_str(f, "<title>"); - if (r < 0) return r; - r = escape_text(f, ctx->sa_names.s + ctx->pages[ctx->cur_page].titleoff, - strlen(ctx->sa_names.s + ctx->pages[ctx->cur_page].titleoff)); - if (r < 0) return r; - r = raw_str(f, "</title>"); - if (r < 0) return r; +#define str_title(i) ctx->sa_names.s + ctx->pages[i].titleoff +#define str_file(i) ctx->sa_names.s + ctx->pages[i].fileoff + if (!raw_str(ctx, "<!DOCTYPE html>\n<html><head>") + || !raw_str(ctx, "<title>") + || !escape_text(ctx, str_title(ctx->cur_page), + strlen(str_title(ctx->cur_page))) + || !raw_str(ctx, "</title>")) + return ERR_IO; if (ctx->options & OPT_INLINE_CSS) { for (int i = 0; i < NB_CSS; ++i) { - if (raw_str(f, "<style>") < 0 - || raw_str(f, ctx->sa_names.s + ctx->css[i].offset) < 0 - || raw_str(f, "</style>") < 0) + if (!raw_str(ctx, "<style>") + || !raw_str(ctx, ctx->sa_names.s + ctx->css[i].offset) + || !raw_str(ctx, "</style>")) return ERR_IO; } } else if (!(ctx->options & OPT_NO_CSS)) { for (int i = 0; i < NB_CSS; ++i) { - r = raw_printf(f, "<link rel=\"stylesheet\" href=\"%s\">", ctx->css[i].file); - if (r < 0) return r; + if (!raw_str(ctx, "<link rel=\"stylesheet\" href=\"") + || !escape_text(ctx, ctx->css[i].file, strlen(ctx->css[i].file)) + || !raw_str(ctx, "\">")) + return ERR_IO; } } - r = raw_str(f, "</head><body><header><nav><ul>"); - if (r < 0) return r; + if (!raw_str(ctx, "</head><body><header><nav><ul>")) + return ERR_IO; for (int i = 0; i < ctx->nb_pages; ++i) { - r = raw_printf(f, "<li><a href=\"%s\">%s</a></li>", - ctx->sa_names.s + ctx->pages[i].fileoff, - ctx->sa_names.s + ctx->pages[i].titleoff); - if (r < 0) return r; + if (!raw_str(ctx, "<li><a href=\"") + || !escape_text(ctx, str_file(i), strlen(str_file(i))) + || !raw_str(ctx, "\">") + || !escape_text(ctx, str_title(i), strlen(str_title(i))) + || !raw_str(ctx, "</a></li>")) + return ERR_IO; } +#undef str_title +#undef str_file - r = raw_str(f, "</ul></nav></header><main>"); - if (r < 0) return r; + if (!raw_str(ctx, "</ul></nav></header><main>")) + return ERR_IO; } break; case MD_BLOCK_QUOTE: - return raw_str(f, "<blockquote>"); + if (!raw_str(ctx, "<blockquote>")) + return ERR_IO; + break; case MD_BLOCK_UL: - return raw_str(f, "<ul>"); + if (!raw_str(ctx, "<ul>")) + return ERR_IO; + break; case MD_BLOCK_OL: - return raw_str(f, "<ol>"); + if (!raw_str(ctx, "<ol>")) + return ERR_IO; + break; case MD_BLOCK_LI: - return raw_str(f, "<li>"); + if (!raw_str(ctx, "<li>")) + return ERR_IO; + break; case MD_BLOCK_HR: - return raw_str(f, "<hr>"); + if (!raw_str(ctx, "<hr>")) + return ERR_IO; + break; case MD_BLOCK_H: { MD_BLOCK_H_DETAIL *d = details; - return raw_printf(f, "<h%d>", d->level); + char buf[UINT32_FMT]; + buf[uint32_fmt(buf, (uint32) d->level)] = '\0'; + if (!raw_str(ctx, "<h") || !raw_str(ctx, buf) || !raw_str(ctx, ">")) + return ERR_IO; } break; @@ -260,7 +274,7 @@ enter_block(MD_BLOCKTYPE type, void *details, void *ctx_) return ERR_INVALID; if (!d->info.text || !strncmp(d->info.text, "pre\n", 4)) { - return raw_str(f, "<pre>"); + return (raw_str(ctx, "<pre>")) ? 0 : ERR_IO; } else { const char *t = d->info.text; size_t l = d->info.size; @@ -303,8 +317,10 @@ enter_block(MD_BLOCKTYPE type, void *details, void *ctx_) } } - if (!(ctx->code.flags & CODE_BUFFERED)) - return raw_str(f, "<pre>"); + if (ctx->code.flags & CODE_BUFFERED) + ctx->buf.state = BUF_WAITING; + else + return (raw_str(ctx, "<pre>")) ? 0 : ERR_IO; } } break; @@ -313,7 +329,9 @@ enter_block(MD_BLOCKTYPE type, void *details, void *ctx_) return ERR_NOTSUPP; case MD_BLOCK_P: - return raw_str(f, "<p>"); + if (!raw_str(ctx, "<p>")) + return ERR_IO; + break; case MD_BLOCK_TABLE: case MD_BLOCK_THEAD: @@ -331,23 +349,32 @@ 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: - return raw_str(f, "</body></html>"); + if (!raw_str(ctx, "</body></html>")) + return ERR_IO; + break; case MD_BLOCK_QUOTE: - return raw_str(f, "</blockquote>"); + if (!raw_str(ctx, "</blockquote>")) + return ERR_IO; + break; case MD_BLOCK_UL: - return raw_str(f, "</ul>"); + if (!raw_str(ctx, "</ul>")) + return ERR_IO; + break; case MD_BLOCK_OL: - return raw_str(f, "</ol>"); + if (!raw_str(ctx, "</ol>")) + return ERR_IO; + break; case MD_BLOCK_LI: - return raw_str(f, "</li>"); + if (!raw_str(ctx, "</li>")) + return ERR_IO; + break; case MD_BLOCK_HR: break; @@ -355,7 +382,10 @@ leave_block(MD_BLOCKTYPE type, void *details, void *ctx_) case MD_BLOCK_H: { MD_BLOCK_H_DETAIL *d = details; - return raw_printf(f, "</h%d>", d->level); + char buf[UINT32_FMT]; + buf[uint32_fmt(buf, (uint32) d->level)] = '\0'; + if (!raw_str(ctx, "</") || !raw_str(ctx, buf) || !raw_str(ctx, ">")) + return ERR_IO; } break; @@ -364,43 +394,49 @@ leave_block(MD_BLOCKTYPE type, void *details, void *ctx_) struct ctx *ctx = ctx_; if (!(ctx->code.flags & CODE_BUFFERED)) { - return raw_str(f, "</pre>"); + return (raw_str(ctx, "</pre>")) ? 0 : ERR_IO; } else { - if (fclose(ctx->buf.f)) return ERR_MEM; - ctx->buf.f = NULL; + const char *buf = ctx->sa_names.s + ctx->buf.salen; + size_t blen = ctx->sa_names.len - ctx->buf.salen; + ctx->sa_names.len = ctx->buf.salen; + ctx->buf.state = BUF_OFF; if (ctx->code.flags & CODE_LINES) { - const char *s = ctx->buf.str; - size_t l = ctx->buf.len; + const char *s = buf; + size_t l = blen; - RAW_OR_RET(f, "<pre class=\"lineno\">"); + if (!raw_str(ctx, "<pre class=\"lineno\">")) + return ERR_IO; for (int n = ctx->code.from; s; ++n) { const char *e = memchr(s, '\n', l); if (!e) break; l -= ++e - s; s = e; - if (raw_printf(f, "%d\n", n) < 0) + char buf[UINT32_FMT]; + buf[uint32_fmt(buf, (uint32) n)] = '\0'; + if (!raw_str(ctx, buf) || !raw_str(ctx, "\n")) return ERR_IO; } - RAW_OR_RET(f, "</pre><code>"); + if (!raw_str(ctx, "</pre><code>")) + return ERR_IO; } else { - RAW_OR_RET(f, "<pre>"); + if (!raw_str(ctx, "<pre>")) + return ERR_IO; } int r; if (ctx->code.flags & CODE_HIGHLIGHT) - r = highlight_escape_text(f, ctx->buf.str, ctx->buf.len); + r = highlight_escape_text(ctx, buf, blen); else - r = escape_text(f, ctx->buf.str, ctx->buf.len); - if (r < 0) return r; + r = escape_text(ctx, buf, blen); + if (!r) return ERR_IO; if (ctx->code.flags & CODE_LINES) - r = raw_str(f, "</code>"); + r = raw_str(ctx, "</code>"); else - r = raw_str(f, "</pre>"); - if (r < 0) return r; + r = raw_str(ctx, "</pre>"); + if (!r) return ERR_IO; - free(ctx->buf.str); ctx->code.flags = 0; } } @@ -410,7 +446,9 @@ leave_block(MD_BLOCKTYPE type, void *details, void *ctx_) return ERR_NOTSUPP; case MD_BLOCK_P: - return raw_str(f, "</p>"); + if (!raw_str(ctx, "</p>")) + return ERR_IO; + break; case MD_BLOCK_TABLE: case MD_BLOCK_THEAD: @@ -428,42 +466,47 @@ 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: - return raw_str(f, "<em>"); + if (!raw_str(ctx, "<em>")) + return ERR_IO; + break; case MD_SPAN_STRONG: - return raw_str(f, "<strong>"); + if (!raw_str(ctx, "<strong>")) + return ERR_IO; + break; case MD_SPAN_A: { MD_SPAN_A_DETAIL *d = details; - RAW_OR_RET(f, "<a href=\""); - r = attribute(f, &d->href); - if (r < 0) return r; + if (!raw_str(ctx, "<a href=\"") || !attribute(ctx, &d->href)) + return ERR_IO; - if (d->title.text) { - RAW_OR_RET(f, "\" title=\""); - r = attribute(f, &d->title); - if (r < 0) return r; - } + if (d->title.text + && (!raw_str(ctx, "\" title=\"") || !attribute(ctx, &d->title))) + return ERR_IO; - return raw_str(f, "\">"); + if (!raw_str(ctx, "\">")) + return ERR_IO; } + break; case MD_SPAN_IMG: /* TODO */ break; case MD_SPAN_CODE: - return raw_str(f, "<tt>"); + if (!raw_str(ctx, "<tt>")) + return ERR_IO; + break; case MD_SPAN_DEL: - return raw_str(f, "<s>"); + if (!raw_str(ctx, "<s>")) + return ERR_IO; + break; case MD_SPAN_LATEXMATH: case MD_SPAN_LATEXMATH_DISPLAY: @@ -471,7 +514,9 @@ enter_span(MD_SPANTYPE type, void *details, void *ctx_) return ERR_NOTSUPP; case MD_SPAN_U: - return raw_str(f, "<u>"); + if (!raw_str(ctx, "<u>")) + return ERR_IO; + break; } return 0; @@ -481,27 +526,36 @@ 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: - return raw_str(f, "</em>"); + if (!raw_str(ctx, "</em>")) + return ERR_IO; + break; case MD_SPAN_STRONG: - return raw_str(f, "</strong>"); + if (!raw_str(ctx, "</strong>")) + return ERR_IO; + break; case MD_SPAN_A: - return raw_str(f, "</a>"); + if (!raw_str(ctx, "</a>")) + return ERR_IO; + break; case MD_SPAN_IMG: /* TODO */ break; case MD_SPAN_CODE: - return raw_str(f, "</tt>"); + if (!raw_str(ctx, "</tt>")) + return ERR_IO; + break; case MD_SPAN_DEL: - return raw_str(f, "</s>"); + if (!raw_str(ctx, "</s>")) + return ERR_IO; + break; case MD_SPAN_LATEXMATH: case MD_SPAN_LATEXMATH_DISPLAY: @@ -509,7 +563,9 @@ leave_span(MD_SPANTYPE type, void *details, void *ctx_) return ERR_NOTSUPP; case MD_SPAN_U: - return raw_str(f, "</u>"); + if (!raw_str(ctx, "</u>")) + return ERR_IO; + break; } return 0; @@ -519,38 +575,39 @@ static int text(MD_TEXTTYPE type, const MD_CHAR *text, MD_SIZE size, void *ctx_) { struct ctx *ctx = ctx_; - FILE *f = ctx->out; switch (type) { case MD_TEXT_NORMAL: - return escape_text(f, text, size); + if (!escape_text(ctx, text, size)) + return ERR_IO; + break; case MD_TEXT_NULLCHAR: break; case MD_TEXT_BR: - return raw_str(f, "<br>"); + if (!raw_str(ctx, "<br>")) + return ERR_IO; + break; case MD_TEXT_SOFTBR: - return raw_str(f, "\n"); + if (!raw_str(ctx, "\n")) + return ERR_IO; + break; case MD_TEXT_ENTITY: - return escape_text(f, text, size); + if (!escape_text(ctx, text, size)) + return ERR_IO; + break; case MD_TEXT_CODE: - { - struct ctx *ctx = ctx_; - - 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; - } - - return raw_text(ctx->buf.f, text, size); + if (ctx->code.flags & CODE_BUFFERED) { + if (!raw_text(ctx, text, size)) + return ERR_IO; + } else if (!escape_text(ctx, text, size)) { + return ERR_IO; } + break; case MD_TEXT_HTML: ; @@ -575,12 +632,13 @@ 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)) { - RAW_OR_RET(f, (tags[i].repl) ? tags[i].repl : tags[i].name); + if (!raw_str(ctx, (tags[i].repl) ? tags[i].repl : tags[i].name)) + return ERR_IO; break; } } - if (i == n) - return escape_text(f, text, size); + if (i == n && !escape_text(ctx, text, size)) + return ERR_IO; break; case MD_TEXT_LATEXMATH: @@ -832,15 +890,17 @@ main (int argc, char *argv[]) int fd = openat_excl(fddest, dst); if (fd < 0) strerr_diefu5sys(-ERR_IO, "create '", destdir, "/", dst, "'"); - ctx.out = fdopen(fd, "we"); - if (!ctx.out) strerr_diefu5sys(-ERR_IO, "open '", destdir, "/", dst, "'"); + char bufout[16 << 10]; + if (!buffer_init(&ctx.out, &fd_writev, fd, bufout, sizeof(bufout))) + strerr_diefu5x(ERR_MISC, "init buffer for '", destdir, "/", dst, "'"); ctx.cur_page = i - optind; int r = convert_page(&ctx); if (r < 0) return -r; - fd_close(pages[ctx.cur_page].fd); - fclose(ctx.out); + if (!buffer_flush(&ctx.out)) + strerr_diefu5sys(-ERR_IO, "flush buffer to '", destdir, "/", dst, "'"); + fd_close(pages[ctx.cur_page].fd); } stralloc_free(&ctx.sa_names);