Welcome to little lamb

Code » qmdoc » commit b48ae90

Finish the skalibs transition

author Olivier Brunel
2022-12-26 23:06:07 UTC
committer Olivier Brunel
2022-12-26 23:08:37 UTC
parent 0fb7e1d14b2a949721de1256a0e074fb38db6a89

Finish the skalibs transition

The output buffer is now a proper buffer (instead of a FILE stream)

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 = "&quot;";
-                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);