Welcome to little lamb

Code » qmdoc » commit ea8ff8e

Refactor things, cleaner interface

author Olivier Brunel
2022-12-24 22:23:42 UTC
committer Olivier Brunel
2022-12-24 22:53:06 UTC
parent a9994fcc41faed6d21c952481b2d6115905e6cbe

Refactor things, cleaner interface

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 = "&amp;";
+                /* fall through */
+            case '<':
+                if (!esc) esc = "&lt;";
+                /* fall through */
+            case '>':
+                if (!esc) esc = "&gt;";
+                /* fall through */
+            case '"':
+                if (!esc) esc = "&quot;";
+                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 = "&amp;";
-                /* fall through */
-            case '<':
-                if (!esc) esc = "&lt;";
-                /* fall through */
-            case '>':
-                if (!esc) esc = "&gt;";
-                /* fall through */
-            case '"':
-                if (!esc) esc = "&quot;";
-                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);