Welcome to little lamb

Code » qmdoc » commit 5394844

Starting adding overall TOC

author Olivier Brunel
2022-12-25 21:52:51 UTC
committer Olivier Brunel
2022-12-25 21:52:51 UTC
parent fca14ddf20e8fe3b5b367b795aecf9531c688e53

Starting adding overall TOC

base.md +1 -1
main.c +98 -4

diff --git a/base.md b/base.md
index e3c0efe..bb90f09 100644
--- a/base.md
+++ b/base.md
@@ -1,4 +1,4 @@
-:Foobar Documentation
+% Foobar Documentation
 
 # Welcome to Foobar !
 
diff --git a/main.c b/main.c
index a03cb19..bf55092 100644
--- a/main.c
+++ b/main.c
@@ -53,10 +53,17 @@ static struct css {
     { "dark.css" }
 };
 
+struct page {
+    char *file;
+    char *title;
+};
+
 struct ctx {
     int options;
     FILE *out;
     struct css *css;
+    struct page *pages;
+    int nb_pages;
     struct {
         int flags;
         int from;
@@ -199,7 +206,16 @@ enter_block(MD_BLOCKTYPE type, void *details, void *ctx_)
                         if (r < 0) return r;
                     }
                 }
-                r = raw_str(f, "</head><body><main>");
+                r = raw_str(f, "</head><body><header><nav><ul>");
+                if (r < 0) return r;
+
+                for (int i = 0; i < ctx->nb_pages; ++i) {
+                    r = raw_printf(f, "<li><a href=\"%s\">%s</a></li>",
+                                   ctx->pages[i].file, ctx->pages[i].title);
+                    if (r < 0) return r;
+                }
+
+                r = raw_str(f, "</ul></nav></header><main>");
                 if (r < 0) return r;
             }
             break;
@@ -599,6 +615,10 @@ convert_file(const char *file, struct ctx *ctx)
     }
     close(fd);
 
+    b = buf;
+    while (b[0] == '%' && b[1] == ' ')
+        b = strchr(b, '\n') + 1;
+
     const MD_PARSER parser = {
         .flags = MD_FLAG_COLLAPSEWHITESPACE | MD_FLAG_PERMISSIVEAUTOLINKS
             | MD_FLAG_NOHTMLBLOCKS | MD_FLAG_STRIKETHROUGH | MD_FLAG_UNDERLINE,
@@ -608,7 +628,7 @@ convert_file(const char *file, struct ctx *ctx)
         .leave_span = leave_span,
         .text = text,
     };
-    int r = md_parse(buf, done, &parser, ctx);
+    int r = md_parse(b, done, &parser, ctx);
     if (r < 0) {
         retx(ERR_PARSER, "parser internal error %d", -r);
     } else if (r > 0) {
@@ -679,6 +699,57 @@ write_file(int fdp, const char *path, const char *file, const char *data)
     return 0;
 }
 
+static int
+load_page_from_file(const char *file, struct page *page)
+{
+    int fd = open(file, O_RDONLY | O_CLOEXEC | O_NONBLOCK);
+    if (fd < 0) ret(ERR_IO, "cannot open '%s'", file);
+
+    FILE *f = fdopen(fd, "re");
+    if (!f) {
+        warn("cannot open '%s'", file);
+        close(fd);
+        return ERR_IO;
+    }
+
+    size_t l = strlen(file);
+    page->file = malloc(l + 3);
+    if (!page->file) {
+        warn("cannot load title from '%s'", file);
+        fclose(f);
+        return ERR_MEM;
+    }
+    memcpy(page->file, file, l - 2);
+    memcpy(page->file + l - 2, "html", 5);
+
+    char buf[256];
+    while (fgets(buf, sizeof(buf), f)) {
+        if (buf[0] != '%' || buf[1] != ' ') {
+            page->title = strdup(file);
+        } else {
+            char *e = memchr(buf + 2, '\n', sizeof(buf) - 2);
+            if (!e) {
+                printf("Title too long in '%s'; Truncated\n", file);
+                e = buf + 512;
+            }
+            size_t l = e - buf - 2;
+            page->title = malloc(l + 1);
+            if (!page->title) {
+                warn("cannot load page title from '%s'", file);
+                fclose(f);
+                free(page->file);
+                return ERR_MEM;
+            }
+            memcpy(page->title, buf + 2, l);
+            page->title[l] = '\0';
+        }
+        break;
+    }
+
+    fclose(f);
+    return 0;
+}
+
 static void
 usage(int err)
 {
@@ -724,6 +795,23 @@ main (int argc, char *argv[])
     if (fddest < 0) err(ERR_IO, "cannot open '%s'", destdir);
 
     int r;
+    struct page pages[argc - optind];
+
+    if (argc > optind + 1) {
+        printf("Scanning files...\n");
+        for (int i = optind; i < argc; ++i) {
+            const char *file = argv[i];
+            size_t len = strlen(file);
+
+            if (strcmp(file + len - 3, ".md")) {
+                printf("Skipping '%s': File must be a markdown file (*.md)\n", file);
+                continue;
+            }
+
+            r = load_page_from_file(file, &pages[i - optind]);
+            if (r < 0) return -r;
+        }
+    }
 
     if (!(options & OPT_NO_CSS)) {
         printf("%s CSS files...\n", (options & OPT_INLINE_CSS) ? "Loading" : "Copying");
@@ -737,14 +825,20 @@ main (int argc, char *argv[])
         }
     }
 
-    struct ctx ctx = { .options = options, .css = css };
+    struct ctx ctx = {
+        .options = options,
+        .css = css,
+        .pages = pages,
+        .nb_pages = sizeof(pages) / sizeof(*pages),
+    };
 
     for (int i = optind; i < argc; ++i) {
         const char *sce = argv[i];
         size_t scelen = strlen(sce);
 
         if (strcmp(sce + scelen - 3, ".md")) {
-            warn("Skipping '%s': File must be a markdown file (*.md)", sce);
+            if (argc == optind + 1)
+                printf("Skipping '%s': File must be a markdown file (*.md)\n", sce);
             continue;
         }