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:
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