saait

Simple static page generator
git clone https://git.sinitax.com/codemadness/saait
Log | Files | Refs | README | LICENSE | Upstream | sfeed.txt

commit 015bc6ccd23e3e1ac78b6373e26d648bff4d08e0
parent aac9d4d389441f56dd883b5eec1aea7672797e27
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Sat, 25 May 2019 20:33:33 +0200

rework templates support

templates are now iterated from the templates directory. It detects the
following prefixes:

"header." - header block
"item."   - item block
"footer." - footer block

when a block is missing it is simply skipped (an empty file is not needed
anymore).

- This now allows adding/remove templates at run-time without having to specify the
templates in config.h and recompile.

- remove listing tcc as supported. It has limited POSIX support and does not
support the required opendir,readdir_r,closedir interfaces.

Diffstat:
MREADME | 2+-
Mconfig.h | 46----------------------------------------------
Msaait.c | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Dtemplates/urllist.txt/footer.txt | 0
Dtemplates/urllist.txt/header.txt | 0
5 files changed, 91 insertions(+), 74 deletions(-)

diff --git a/README b/README @@ -8,7 +8,7 @@ Some parts are intentionally hardcoded, C knowledge is required. Dependencies: ------------- -- C compiler (C99), tested on gcc, clang, tcc. +- C compiler (C99), tested on gcc, clang. - libc diff --git a/config.h b/config.h @@ -16,49 +16,3 @@ struct template { /* output FILE * (set at runtime) */ FILE *fp; }; - -static struct template templates[] = { - /* special: will be applied per page */ - { - .name = "page", .blocks = { - { .name = "page/header.html" }, - { .name = "page/item.html" }, - { .name = "page/footer.html" } - } - }, - { - .name = "atom.xml", .blocks = { - { .name = "atom.xml/header.xml" }, - { .name = "atom.xml/item.xml" }, - { .name = "atom.xml/footer.xml" } - } - }, - { - .name = "index.html", .blocks = { - { .name = "index.html/header.html" }, - { .name = "index.html/item.html" }, - { .name = "index.html/footer.html" } - } - }, - { - .name = "sitemap.xml", .blocks = { - { .name = "sitemap.xml/header.xml" }, - { .name = "sitemap.xml/item.xml" }, - { .name = "sitemap.xml/footer.xml" } - } - }, - { - .name = "urllist.txt", .blocks = { - { .name = "urllist.txt/header.txt" }, - { .name = "urllist.txt/item.txt" }, - { .name = "urllist.txt/footer.txt" } - } - }, - { - .name = "twtxt.txt", .blocks = { - { .name = "twtxt.txt/header.txt" }, - { .name = "twtxt.txt/item.txt" }, - { .name = "twtxt.txt/footer.txt" } - } - } -}; diff --git a/saait.c b/saait.c @@ -1,7 +1,11 @@ +#include <sys/types.h> + #include <ctype.h> +#include <dirent.h> #include <errno.h> #include <limits.h> #include <stdio.h> +#include <stdint.h> #include <stdlib.h> #include <string.h> @@ -371,12 +375,15 @@ usage(void) int main(int argc, char *argv[]) { + struct template *t, *templates = NULL; struct block *b; struct variable *c, *v; - char file[PATH_MAX + 1], htmlfile[PATH_MAX + 1]; - char outputfile[PATH_MAX + 1], *p; - size_t i, j, k; - int r; + DIR *bdir, *idir; + struct dirent be, ie, *br, *ir; + char file[PATH_MAX + 1], htmlfile[PATH_MAX + 1], path[PATH_MAX + 1]; + char outputfile[PATH_MAX + 1], *p, *filename; + size_t i, j, k, templateslen; + int r, doindex; if (pledge("stdio cpath rpath wpath", NULL) == -1) { fprintf(stderr, "pledge: %s\n", strerror(errno)); @@ -400,23 +407,69 @@ main(int argc, char *argv[]) /* global config */ global = readconfig(configfile); - /* load templates: all templates must be loaded correctly first. */ - for (i = 0; i < LEN(templates); i++) { - for (j = 0; j < LEN(templates[i].blocks); j++) { - b = &templates[i].blocks[j]; - r = snprintf(file, sizeof(file), "%s/%s", templatedir, - b->name); + /* load templates, must start with header., item. or footer. */ + templateslen = 0; + if (!(bdir = opendir(templatedir))) { + fprintf(stderr, "opendir: %s: %s\n", templatedir, strerror(errno)); + exit(1); + } + + while (!readdir_r(bdir, &be, &br) && br) { + if (br->d_name[0] == '.') + continue; + + r = snprintf(path, sizeof(path), "%s/%s", templatedir, + br->d_name); + if (r < 0 || (size_t)r >= sizeof(path)) { + fprintf(stderr, "path truncated: '%s/%s'\n", + templatedir, br->d_name); + exit(1); + } + + if (!(idir = opendir(path))) { + fprintf(stderr, "opendir: %s: %s\n", path, strerror(errno)); + exit(1); + } + + templateslen++; + /* check overflow */ + if (SIZE_MAX / templateslen < sizeof(*templates)) { + fprintf(stderr, "realloc: too many templates: %zu\n", templateslen); + exit(1); + } + templates = realloc(templates, templateslen * sizeof(*templates)); + t = &templates[templateslen - 1]; + memset(t, 0, sizeof(struct template)); + t->name = estrdup(br->d_name); + + while (!readdir_r(idir, &ie, &ir) && ir) { + if (!strncmp(ir->d_name, "header.", sizeof("header.") - 1)) + b = &(t->blocks[BlockHeader]); + else if (!strncmp(ir->d_name, "item.", sizeof("item.") - 1)) + b = &(t->blocks[BlockItem]); + else if (!strncmp(ir->d_name, "footer.", sizeof("footer.") - 1)) + b = &(t->blocks[BlockFooter]); + else + continue; + + r = snprintf(file, sizeof(file), "%s/%s", path, + ir->d_name); if (r < 0 || (size_t)r >= sizeof(file)) { fprintf(stderr, "path truncated: '%s/%s'\n", - templatedir, b->name); + path, ir->d_name); exit(1); } b->data = readfile(file); + b->name = estrdup(file); } + closedir(idir); } + closedir(bdir); /* header */ - for (i = 0; i < LEN(templates); i++) { + for (i = 0; i < templateslen; i++) { + /* TODO: document special "page". + TODO: make "page" template mandatory? */ if (!strcmp(templates[i].name, "page")) continue; r = snprintf(file, sizeof(file), "%s/%s", outputdir, @@ -428,13 +481,17 @@ main(int argc, char *argv[]) } templates[i].fp = efopen(file, "wb"); b = &templates[i].blocks[BlockHeader]; - writepage(templates[i].fp, b->name, NULL, b->data); + if (b->name) + writepage(templates[i].fp, b->name, NULL, b->data); } /* pages */ for (i = 0; i < (size_t)argc; i++) { c = readconfig(argv[i]); + v = getvar(c, "index"); + doindex = !(v && v->value[0] == '0'); + if ((p = strrchr(argv[i], '.'))) r = snprintf(htmlfile, sizeof(htmlfile), "%.*s.html", (int)(p - argv[i]), argv[i]); @@ -450,35 +507,40 @@ main(int argc, char *argv[]) /* set HTML output filename (with part removed), but allow to override it */ if ((p = strrchr(htmlfile, '/'))) - setvar(&c, newvar("filename", &htmlfile[p - htmlfile + 1]), 0); + filename = &htmlfile[p - htmlfile + 1]; else - setvar(&c, newvar("filename", htmlfile), 0); + filename = htmlfile; - /* get output filename */ - v = getvar(c, "filename"); + if ((v = getvar(c, "filename"))) + filename = v->value; + else + setvar(&c, newvar("filename", filename), 0); - /* item block */ - for (j = 0; j < LEN(templates); j++) { + /* item blocks */ + for (j = 0; j < templateslen; j++) { /* TODO: page is a special case for now */ if (!strcmp(templates[j].name, "page")) { + /* TODO: config option to not process as page (opposite of "index" option)? */ r = snprintf(outputfile, sizeof(outputfile), "%s/%s", - outputdir, v->value); + outputdir, filename); if (r < 0 || (size_t)r >= sizeof(outputfile)) { fprintf(stderr, "path truncated: '%s/%s'\n", - outputdir, v->value); + outputdir, filename); exit(1); } + /* "page" template files are opened per item + as opposed to other templates */ templates[j].fp = efopen(outputfile, "wb"); for (k = 0; k < LEN(templates[j].blocks); k++) { b = &templates[j].blocks[k]; - writepage(templates[j].fp, b->name, c, b->data); + if (b->name) + writepage(templates[j].fp, b->name, c, b->data); } fclose(templates[j].fp); } else { - v = getvar(c, "index"); - if (v && v->value[0] == '0') - continue; + if (!doindex) + continue; /* do not include in index */ b = &templates[j].blocks[BlockItem]; writepage(templates[j].fp, b->name, c, b->data); } @@ -486,11 +548,12 @@ main(int argc, char *argv[]) freevars(c); } - for (i = 0; i < LEN(templates); i++) { + for (i = 0; i < templateslen; i++) { if (!strcmp(templates[i].name, "page")) continue; b = &templates[i].blocks[BlockFooter]; - writepage(templates[i].fp, b->name, NULL, b->data); + if (b->name) + writepage(templates[i].fp, b->name, NULL, b->data); } return 0; diff --git a/templates/urllist.txt/footer.txt b/templates/urllist.txt/footer.txt diff --git a/templates/urllist.txt/header.txt b/templates/urllist.txt/header.txt