rework templates support - saait - the most boring static page generator | |
git clone git://git.codemadness.org/saait | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 015bc6ccd23e3e1ac78b6373e26d648bff4d08e0 | |
parent aac9d4d389441f56dd883b5eec1aea7672797e27 | |
Author: Hiltjo Posthuma <[email protected]> | |
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… | |
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: | |
M README | 2 +- | |
M config.h | 46 -----------------------------… | |
M saait.c | 117 ++++++++++++++++++++++++-----… | |
D templates/urllist.txt/footer.txt | 0 | |
D templates/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(err… | |
+ 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(er… | |
+ exit(1); | |
+ } | |
+ | |
+ templateslen++; | |
+ /* check overflow */ | |
+ if (SIZE_MAX / templateslen < sizeof(*templates)) { | |
+ fprintf(stderr, "realloc: too many templates: %zu\n", … | |
+ exit(1); | |
+ } | |
+ templates = realloc(templates, templateslen * sizeof(*template… | |
+ 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.") … | |
+ b = &(t->blocks[BlockHeader]); | |
+ else if (!strncmp(ir->d_name, "item.", sizeof("item.")… | |
+ b = &(t->blocks[BlockItem]); | |
+ else if (!strncmp(ir->d_name, "footer.", sizeof("foote… | |
+ 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 +… | |
+ 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 … | |
r = snprintf(outputfile, sizeof(outputfile), "… | |
- outputdir, v->value); | |
+ outputdir, filename); | |
if (r < 0 || (size_t)r >= sizeof(outputfile)) { | |
fprintf(stderr, "path truncated: '%s/%… | |
- 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,… | |
+ if (b->name) | |
+ writepage(templates[j].fp, b->… | |
} | |
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 |