author | Olivier Brunel
<jjk@jjacky.com> 2023-01-06 09:57:24 UTC |
committer | Olivier Brunel
<jjk@jjacky.com> 2023-01-06 10:48:57 UTC |
parent | c2ad0725827c0c158c2899b20f2c99da9cbaf258 |
common.css | +2 | -2 |
dark.css | +2 | -2 |
light.css | +2 | -2 |
main.c | +86 | -11 |
struct.css | +45 | -2 |
diff --git a/common.css b/common.css index 5cb09ed..fe0dfb2 100644 --- a/common.css +++ b/common.css @@ -92,12 +92,12 @@ main div.box > :first-child::before { main div.box > :first-child { font-weight: 700; } -main footer { +main footer.page { margin-top: 16px; opacity: 0.8; font-size: 80%; } -main footer .generated { +main footer.page .generated { opacity: 0.8; font-size: 90%; } diff --git a/dark.css b/dark.css index 5b8cdf5..787cdcf 100644 --- a/dark.css +++ b/dark.css @@ -40,7 +40,7 @@ main section > ul.toc > li > ul > li > ul > li > ul > li > ul > li > ul > li > ul > li > a { color: #0f0b37; } - main, main footer { + main, main footer.page { background: #80732d; } main { @@ -110,7 +110,7 @@ color: inherit; box-shadow: inset 0 1px 2px -1px hsla(0,0%,100%,.5),inset 0 -2px 0 0 #232323; } - main footer { + main footer.page { border-top: 2px solid #515151; } main pre code { diff --git a/light.css b/light.css index d3a12c5..480dde8 100644 --- a/light.css +++ b/light.css @@ -41,7 +41,7 @@ main section > ul.toc > li > ul > li > ul > li > ul > li > ul > li > ul > li > u main section > ul.toc a:hover { color: blue; } -main, main footer { +main, main footer.page { background: #fbf4ce; } main { @@ -111,6 +111,6 @@ main #navbuttons a { color: inherit; box-shadow: inset 0 1px 2px -1px hsla(0,0%,100%,.5),inset 0 -2px 0 0 #232323; } -main footer { +main footer.page { border-top: 1px solid #979518; } diff --git a/main.c b/main.c index f7051ce..d72439f 100644 --- a/main.c +++ b/main.c @@ -55,6 +55,9 @@ struct page { const char *sce; size_t fileoff; size_t titleoff; + size_t nameoff; + size_t veroff; + size_t dateoff; int fd; size_t size; }; @@ -307,7 +310,10 @@ enter_block(MD_BLOCKTYPE type, void *details, void *ctx_) switch (type) { case MD_BLOCK_DOC: { -#define str_title(i) ctx->sa.s + ctx->pages[i].titleoff + struct page *p = &ctx->pages[ctx->cur_page]; + +#define offset(i) ((ctx->pages[i].nameoff) ? ctx->pages[i].nameoff : ctx->pages[i].titleoff) +#define str_title(i) ctx->sa.s + offset(i) #define str_file(i) ctx->sa.s + ctx->pages[i].fileoff size_t authlen = strlen(ctx->doc.author); if (!raw_str(ctx, "<!DOCTYPE html>\n<html lang=\"") @@ -317,9 +323,16 @@ enter_block(MD_BLOCKTYPE type, void *details, void *ctx_) || (authlen && (!raw_str(ctx, "<meta name=\"author\" content=\"") || !escape_text(ctx, ctx->doc.author, authlen) || !raw_str(ctx, "\">"))) - || !raw_str(ctx, "<title>") - || !escape_text(ctx, str_title(ctx->cur_page), - strlen(str_title(ctx->cur_page))) + || !raw_str(ctx, "<title>")) + return ERR_PARSER_ENTER_BLOCK; + if (p->nameoff) { + if (!escape_text(ctx, ctx->sa.s + p->nameoff, + strlen(ctx->sa.s + p->nameoff)) + || !raw_str(ctx, " - ")) + return ERR_PARSER_ENTER_BLOCK; + } + if (!escape_text(ctx, ctx->sa.s + p->titleoff, + strlen(ctx->sa.s + p->titleoff)) || !raw_str(ctx, "</title>")) return ERR_PARSER_ENTER_BLOCK; if (ctx->options & OPT_INLINE_CSS) { @@ -408,6 +421,20 @@ enter_block(MD_BLOCKTYPE type, void *details, void *ctx_) if (!raw_str(ctx, "<section>")) return ERR_PARSER_ENTER_BLOCK; + + if (p->nameoff) { + if (!raw_str(ctx, "<header><div>") + || !escape_text(ctx, ctx->sa.s + p->nameoff, + strlen(ctx->sa.s + p->nameoff)) + || !raw_str(ctx, "</div><div>") + || !escape_text(ctx, ctx->sa.s + p->titleoff, + strlen(ctx->sa.s + p->titleoff)) + || !raw_str(ctx, "</div><div>") + || !escape_text(ctx, ctx->sa.s + p->nameoff, + strlen(ctx->sa.s + p->nameoff)) + || !raw_str(ctx, "</div></header>")) + return ERR_PARSER_ENTER_BLOCK; + } #undef str_title #undef str_file } @@ -575,6 +602,8 @@ leave_block(MD_BLOCKTYPE type, void *details, void *ctx_) switch (type) { case MD_BLOCK_DOC: ; + struct page *p = &ctx->pages[ctx->cur_page]; + char year[UINT32_FMT]; struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); @@ -583,6 +612,19 @@ leave_block(MD_BLOCKTYPE type, void *details, void *ctx_) year[uint32_fmt(year, (uint32) 1900 + tm.tm_year)] = '\0'; if ((ctx->doc.flags & DOC_HAS_TITLE) && !raw_str(ctx, "</section>")) return ERR_PARSER_LEAVE_BLOCK; + if (p->nameoff) { + if (!raw_str(ctx, "<footer><div>") + || !escape_text(ctx, ctx->sa.s + p->veroff, + strlen(ctx->sa.s + p->veroff)) + || !raw_str(ctx, "</div><div>") + || !escape_text(ctx, ctx->sa.s + p->dateoff, + strlen(ctx->sa.s + p->dateoff)) + || !raw_str(ctx, "</div><div>") + || !escape_text(ctx, ctx->sa.s + p->nameoff, + strlen(ctx->sa.s + p->nameoff)) + || !raw_str(ctx, "</div></footer>")) + return ERR_PARSER_ENTER_BLOCK; + } if (ctx->options & OPT_BUTTONS) { #define str_title(i) ctx->sa.s + ctx->pages[i].titleoff #define str_file(i) ctx->sa.s + ctx->pages[i].fileoff @@ -611,7 +653,7 @@ leave_block(MD_BLOCKTYPE type, void *details, void *ctx_) #undef str_file #undef str_title } - if (!raw_str(ctx, "<footer>Copyright © ") + if (!raw_str(ctx, "<footer class=\"page\">Copyright © ") || !raw_str(ctx, year) || !raw_str(ctx, " ") || !escape_text(ctx, ctx->doc.author, strlen(ctx->doc.author)) @@ -1091,6 +1133,14 @@ convert_page(struct ctx *ctx, int fddest) return 0; } +static int +empty(const char *s) +{ + for ( ; *s; ++s) + if (*s != ' ' && *s != '\t') return 0; + return 1; +} + static int load_page_from_file(const char *file, struct page *page, stralloc *sa) { @@ -1124,17 +1174,19 @@ load_page_from_file(const char *file, struct page *page, stralloc *sa) if (begin) is_hdr = (b[0] == '%' && b[1] == ' '); if (begin && !is_hdr) break; + size_t *offset = NULL; switch (line) { case 1: - if (begin && is_hdr) page->titleoff = sa->len; - if (is_hdr - && (!stralloc_catb(sa, b + ((is_hdr) ? 2 : 0), - ((e) ? e - b : left) - ((is_hdr) ? 2 : 0)) - || (e && !stralloc_0(sa)))) - retwusys(ERR_MEM, "load page title from '", file, "'"); + offset = &page->titleoff; break; case 2: + offset = &page->nameoff; + break; case 3: + offset = &page->veroff; + break; + case 4: + offset = &page->dateoff; break; default: if (begin && is_hdr && !warned) { @@ -1142,6 +1194,14 @@ load_page_from_file(const char *file, struct page *page, stralloc *sa) warned = 1; } } + if (offset) { + if (begin && is_hdr) *offset = sa->len; + if (is_hdr + && (!stralloc_catb(sa, b + ((is_hdr) ? 2 : 0), + ((e) ? e - b : left) - ((is_hdr) ? 2 : 0)) + || (e && !stralloc_0(sa)))) + retwusys(ERR_MEM, "load page title from '", file, "'"); + } if (e) { int l = e - b + 1; done += l; @@ -1161,6 +1221,19 @@ load_page_from_file(const char *file, struct page *page, stralloc *sa) } } + if (empty(sa->s + page->titleoff)) + page->titleoff = page->fileoff; + + if (page->nameoff && empty(sa->s + page->nameoff)) + page->nameoff = 0; + + if (page->nameoff) { + if (!page->veroff || empty(sa->s + page->veroff)) + page->veroff = page->nameoff; + if (!page->dateoff || empty(sa->s + page->dateoff)) + page->dateoff = page->titleoff; + } + page->size = lseek(page->fd, 0, SEEK_END); if (page->size == (off_t) -1 || lseek(page->fd, done, SEEK_SET) < 0) retwusys(ERR_MEM, "seek into '", file, "'"); @@ -1305,6 +1378,7 @@ main (int argc, char *argv[]) struct page pages[argc - optind + 1]; ctx.pages = pages; ctx.nb_pages = sizeof(pages) / sizeof(*pages) - 1; + memset(pages, 0, ctx.nb_pages * sizeof(*pages)); int err = 0; int idx_page = -1; @@ -1354,6 +1428,7 @@ main (int argc, char *argv[]) /* fd == -1 means use index_md instead of reading from fd */ pages[0].fd = -1; pages[0].size = strlen(index_md); + pages[0].nameoff = pages[0].veroff = pages[0].dateoff = 0; ++ctx.nb_pages; } else if (idx_page > 0) { /* move index's page to first */ diff --git a/struct.css b/struct.css index c31a5a7..a3d38e9 100644 --- a/struct.css +++ b/struct.css @@ -25,6 +25,32 @@ main header { main > section { margin-left: 300px; } +main > section > header { + width: 100%; +} +main > section > header > div, +main > section > footer > div { + width: 100%; + position: absolute; +} +main > section > header > div + div, +main > section > footer > div + div { + text-align: center; + width: 100%; + position: absolute; +} +main > section > header > div + div + div, +main > section > footer > div + div + div { + text-align: right; + width: 100%; + position: absolute; +} +main > section > header { + margin-bottom: 23px; +} +main > section > footer { + margin-top: 23px; +} @supports (display: grid) { main { display: grid; @@ -41,6 +67,20 @@ main > section { main > section { margin-left: inherit; } + main > section > header, main > section > footer { + display: grid; + grid-template-areas: "left middle right"; + grid-template-columns: 1fr 2fr 1fr; + } + main > section > header > div, + main > section > footer > div, + main > section > header > div + div, + main > section > footer > div + div, + main > section > header > div + div + div, + main > section > footer > div + div + div { + width: inherit; + position: inherit; + } } main header section { position: sticky; @@ -172,7 +212,7 @@ main pre.lineno + pre { } main pre.lineno + pre > span { position: absolute; - left: 55px; + margin-left: -8px; margin-top: -2px; padding: 0 23px; font-weight: 600; @@ -277,7 +317,10 @@ main #navbuttons a.next { main #navbuttons a.next::after { content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASBAMAAACk4JNkAAAAJFBMVEUAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEC4lvDfAAAAC3RSTlMAx711ZjlFPh3zLASjkrYAAABDSURBVAjXY6AUsGhvcgAzOKR3797YAGIx7t5mvVsAxPLevZ159xYQS3v3zgLrTSDW7tTQBcy7ESyELEIHwhSEyQjbAAH1HsMY8tCHAAAAAElFTkSuQmCC); } -main footer { +main footer.page { + display: inherit; +} +main footer.page { padding: 8px 23px; margin: 0 -23px; }