author | Olivier Brunel
<jjk@jjacky.com> 2022-12-26 19:25:16 UTC |
committer | Olivier Brunel
<jjk@jjacky.com> 2022-12-26 19:39:02 UTC |
parent | 083d8979d23c0a9a32104f2efaece990086c172a |
main.c | +112 | -88 |
diff --git a/main.c b/main.c index 6523780..6e74073 100644 --- a/main.c +++ b/main.c @@ -42,7 +42,7 @@ enum { }; static struct css { const char *file; - off_t offset; + size_t offset; } css[NB_CSS] = { { "struct.css" }, { "common.css" }, @@ -51,14 +51,16 @@ static struct css { }; struct page { - char *file; - char *title; + size_t fileoff; + size_t titleoff; + int fd; + size_t size; }; struct ctx { int options; FILE *out; - stralloc sa_css; + stralloc sa_names; struct css *css; struct page *pages; int nb_pages; @@ -191,10 +193,17 @@ enter_block(MD_BLOCKTYPE type, void *details, void *ctx_) { 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[0].titleoff, + strlen(ctx->sa_names.s + ctx->pages[0].titleoff)); + if (r < 0) return r; + r = raw_str(f, "</title>"); + if (r < 0) return r; 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_css.s + ctx->css[i].offset) < 0 + || raw_str(f, ctx->sa_names.s + ctx->css[i].offset) < 0 || raw_str(f, "</style>") < 0) return ERR_IO; } @@ -209,7 +218,8 @@ enter_block(MD_BLOCKTYPE type, void *details, void *ctx_) 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); + ctx->sa_names.s + ctx->pages[i].fileoff, + ctx->sa_names.s + ctx->pages[i].titleoff); if (r < 0) return r; } @@ -626,7 +636,7 @@ convert_file(const char *file, struct ctx *ctx) .leave_span = leave_span, .text = text, }; - int r = md_parse(b, done, &parser, ctx); + int r = md_parse(b, done - (b - buf), &parser, ctx); if (r < 0) { retx(ERR_PARSER, "parser internal error %d", -r); } else if (r > 0) { @@ -636,55 +646,78 @@ convert_file(const char *file, struct ctx *ctx) return 0; } -static int -load_page_from_file(const char *file, struct page *page) +static void +load_page_from_file(const char *file, struct page *page, stralloc *sa_names) { - 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; - } + page->fd = open_read(file); + if (page->fd < 0) strerr_diefu3sys(-ERR_IO, "open '", file, "'"); 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'; + page->fileoff = sa_names->len; + if (!stralloc_catb(sa_names, file, l - 2) + || !stralloc_catb(sa_names, "html", 5)) + strerr_diefu3sys(-ERR_MEM, "load page title from '", file, "'"); + + char buf_[256], buf[sizeof(buf_)]; + buffer_t buffer = BUFFER_INIT(&fd_readv, page->fd, buf_, sizeof(buf_)); + + ssize_t left = buffer_get(&buffer, buf, sizeof(buf)); + if (left <= 0) + strerr_diefu3sys(-ERR_IO, "load page title from '", file, "'"); + + page->titleoff = page->fileoff; + + char *b = buf; + int line = 1, begin = 1, is_hdr = 0; + int done = 0, warned = 0; + while (left > 0) { + char *e = memchr(b + 2, '\n', left - 2); + if (begin) is_hdr = (b[0] == '%' && b[1] == ' '); + if (begin && !is_hdr) + break; + switch (line) { + case 1: + if (begin && is_hdr) page->titleoff = sa_names->len; + if (is_hdr + && (!stralloc_catb(sa_names, b + ((is_hdr) ? 2 : 0), + ((e) ? e - b : left) - ((is_hdr) ? 2 : 0)) + || (e && !stralloc_0(sa_names)))) + strerr_diefu3sys(-ERR_MEM, "load page title from '", file, "'"); + break; + case 2: + case 3: + break; + default: + if (begin && is_hdr && !warned) { + errs("warning: header too long in '"); + errs(file); + errse("': Only 3 lines supported"); + warned = 1; + } + } + if (e) { + int l = e - b + 1; + done += l; + b += l; + left -= l; + begin = 1; + is_hdr = 0; + ++line; + } + if (!e || left == 0) { + done += left; + b = buf; + left = buffer_get(&buffer, buf, sizeof(buf)); + if (left <= 0) + strerr_diefu3sys(-ERR_IO, "load page title from '", file, "'"); + if (!e) begin = 0; } - break; } - fclose(f); - return 0; + page->size = lseek(page->fd, 0, SEEK_END); + if (page->size == (off_t) -1 || lseek(page->fd, done, SEEK_SET) < 0) + strerr_diefu3sys(-ERR_MEM, "seek into '", file, "'"); + page->size -= done; } static void @@ -743,51 +776,47 @@ main (int argc, char *argv[]) int fddest = open(destdir, O_RDONLY | O_DIRECTORY | O_CLOEXEC); if (fddest < 0) strerr_diefu3sys(-ERR_IO, "open '", destdir, "'"); - int r; struct page pages[argc - optind]; - - if (argc > optind + 1) { - outse("Scanning files..."); - for (int i = optind; i < argc; ++i) { - const char *file = argv[i]; - size_t len = strlen(file); - - if (strcmp(file + len - 3, ".md")) { - outs("Skipping '"); - outs(file); - outse("': File must be a markdown file (*.md)"); - continue; - } - - r = load_page_from_file(file, &pages[i - optind]); - if (r < 0) return -r; - } - } - struct ctx ctx = { .options = options, - .sa_css = STRALLOC_ZERO, + .sa_names = STRALLOC_ZERO, .css = css, .pages = pages, .nb_pages = sizeof(pages) / sizeof(*pages), }; + outse("Scanning files..."); + for (int i = optind; i < argc; ++i) { + const char *file = argv[i]; + size_t len = strlen(file); + + if (strcmp(file + len - 3, ".md")) { + outs("Skipping '"); + outs(file); + outse("': File must be a markdown file (*.md)"); + pages[i - optind].fd = -1; + continue; + } + + load_page_from_file(file, &pages[i - optind], &ctx.sa_names); + } + if (!(options & OPT_NO_CSS)) { outs((options & OPT_INLINE_CSS) ? "Loading" : "Copying"); outse(" CSS files..."); for (int i = 0; i < NB_CSS; ++i) { - if ((options & OPT_INLINE_CSS)) { - css[i].offset = ctx.sa_css.len; - if (!openreadfileclose(css[i].file, &ctx.sa_css, 0)) - strerr_diefu3sys(ERR_IO, "load CSS from '", css[i].file, "'"); + if (options & OPT_INLINE_CSS) { + css[i].offset = ctx.sa_names.len; + if (!openreadfileclose(css[i].file, &ctx.sa_names, 0)) + strerr_diefu3sys(-ERR_IO, "load CSS from '", css[i].file, "'"); } else { int from, to; from = open_read(css[i].file); - if (from < 0) strerr_diefu3sys(ERR_IO, "open '", css[i].file, "'"); + if (from < 0) strerr_diefu3sys(-ERR_IO, "open '", css[i].file, "'"); to = openat_excl(fddest, css[i].file); - if (to < 0) strerr_diefu5sys(ERR_IO, "create '", destdir, "/", css[i].file, "'"); + if (to < 0) strerr_diefu5sys(-ERR_IO, "create '", destdir, "/", css[i].file, "'"); if (fd_cat(from, to) < 0) - strerr_diefu5sys(ERR_IO, "copy CSS to '", destdir, "/", css[i].file, "'"); + strerr_diefu5sys(-ERR_IO, "copy CSS to '", destdir, "/", css[i].file, "'"); fd_close(from); fd_close(to); } @@ -798,14 +827,7 @@ main (int argc, char *argv[]) const char *sce = argv[i]; size_t scelen = strlen(sce); - if (strcmp(sce + scelen - 3, ".md")) { - if (argc == optind + 1) { - outs("Skipping '"); - outs(sce); - outse("': File must be a markdown file (*.md)\n"); - } - continue; - } + if (pages[i - optind].fd < 0) continue; char dst[scelen + 3]; memcpy(dst, sce, scelen - 2); @@ -815,18 +837,20 @@ main (int argc, char *argv[]) outs(sce); outse("..."); - int fd = openat(fddest, dst, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC | O_NONBLOCK, 0644); + 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, "'"); - r = convert_file(sce, &ctx); + int r = convert_file(sce, &ctx); if (r < 0) return -r; fclose(ctx.out); } + stralloc_free(&ctx.sa_names); + outse("done."); return 0; }