ssnail

crappy and opinionated static site generator
git clone https://git.e1e0.net/ssnail.git
Log | Files | Refs | README | LICENSE

commit d42fe37ed17a238e006cff6967105da64dd36b13
parent c45e2fd11bf7b654631b1135baf8c4e8abf48a66
Author: Paco Esteban <paco@e1e0.net>
Date:   Wed,  1 Jul 2020 16:37:10 +0200

add rss generation

Diffstat:
Mhelpers.h | 1+
Mssnail.c | 132++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 128 insertions(+), 5 deletions(-)

diff --git a/helpers.h b/helpers.h @@ -20,6 +20,7 @@ #define HEADER "_header.html" #define FOOTER "_footer.html" #define SHORT_DATE_Z 12 +#define LONG_DATE_Z 32 const struct ssnail_error *copy_file(const char *, const char *, int); diff --git a/ssnail.c b/ssnail.c @@ -58,6 +58,7 @@ __dead static void usage(void); static void free_article(struct article *); static void free_articleq(struct listhead *); static void add_index_entry(char **, struct article *); +static void add_rss_entry(char **, struct article *, char *); struct article *init_article(void); static struct article *populate_article_entry(char *, char *); @@ -67,12 +68,13 @@ static const struct ssnail_error *process_dir(char *, char *, int); static const struct ssnail_error *write_html(struct article *, char *, char *); static const struct ssnail_error *sort_articleq(struct listhead *); static const struct ssnail_error *gen_html(struct article *); +static const struct ssnail_error *write_rss(char *, char *, char *, char *); __dead static void usage(void) { - fprintf(stderr, "usage: %s [-F] [-f footer] [-h header] [-i] " - "src_folder dst_folder\n", getprogname()); + fprintf(stderr, "usage: %s [-F] [-f footer] [-h header] [-i] [-r] " + "-t <title> -u <url> src_folder dst_folder\n", getprogname()); exit(1); } @@ -82,9 +84,10 @@ main(int argc, char *argv[]) struct article *ap = NULL, *ip = NULL; const struct ssnail_error *error = NULL; - int ch, index = 0, force = 0; + int ch, index = 0, rss = 0, force = 0; char *header_tpl = NULL, *footer_tpl = NULL, - *index_listing = ""; + *index_listing = "", *rss_listing = "", + *title = NULL, *url = NULL; header_tpl = strdup(HEADER); if (header_tpl == NULL) { @@ -97,7 +100,7 @@ main(int argc, char *argv[]) goto done; } - while ((ch = getopt(argc, argv, "Ff:h:i")) != -1) { + while ((ch = getopt(argc, argv, "Ff:h:irt:u:")) != -1) { switch (ch) { case 'F': force = 1; @@ -121,6 +124,23 @@ main(int argc, char *argv[]) case 'i': index = 1; break; + case 'r': + rss = 1; + break; + case 't': + title = strdup(optarg); + if (title == NULL) { + error = ssnail_error_from_errno("title"); + goto done; + } + break; + case 'u': + url = strdup(optarg); + if (url == NULL) { + error = ssnail_error_from_errno("url"); + goto done; + } + break; default: usage(); } @@ -130,6 +150,9 @@ main(int argc, char *argv[]) LIST_INIT(&head); /* init the linked list */ + /* title and usage are mandatory */ + if (!title || !url) + usage(); /* src and dst dir are mandatory. */ if (argc < 2) usage(); @@ -147,9 +170,12 @@ main(int argc, char *argv[]) error = write_html(ap, header_tpl, footer_tpl); if (error) goto done; + index = 1; + rss = 1; } if (strcmp(ap->type, "article") == 0) { add_index_entry(&index_listing, ap); + add_rss_entry(&rss_listing, ap, url); } } @@ -161,12 +187,20 @@ main(int argc, char *argv[]) goto done; } error = write_html(ip, header_tpl, footer_tpl); + if (error) + goto done; + } + + if (rss || force) { + printf("Generate rss ... \n"); + error = write_rss(argv[1], rss_listing, title, url); } done: free(header_tpl); free(footer_tpl); free(index_listing); + free(rss_listing); free_article(ip); free_articleq(&head); @@ -562,3 +596,91 @@ init_article(void) return a; } + +static void +add_rss_entry(char **rss_listing, struct article *a, char *url) +{ + if (!rss_listing || !a) + return; + + struct tm timeinfo; + char *content = NULL, *html_file = NULL, + *link = NULL, pub_date[LONG_DATE_Z]; + const char *entry_format = + "%s<item>\n" + "<guid>%s</guid>\n" + "<link>%s</link>\n" + "<author>%s</author>\n" + "<title>%s</title>\n" + "<pubDate>%s</pubDate>\n" + "<description>\n%s\n</description>\n" + "</item>\n"; + + if (strptime(a->date, "%Y-%m-%d", &timeinfo) == NULL) + goto out; + + /* + * we don't keep the hour and tz values on the markdown metadata, so set + * it to 00h GMT. Which will never be too off from reality. + */ + if (strftime(pub_date, LONG_DATE_Z, "%a, %d %b %Y 00:00:00 GMT", &timeinfo) == 0) + goto out; + + content = strndup(a->html_content, a->htmlz); + html_file = strstr(a->dst_path, "/") + 1; + if (html_file) + link = build_full_path(url, html_file); + + if (link && content) + asprintf(rss_listing, entry_format, *rss_listing, + link, link, a->author, a->title, pub_date, content); + +out: + free(link); + free(content); +} + +static const struct ssnail_error * +write_rss(char *dst_path, char *rss_listing, char *title, char *url) +{ + const struct ssnail_error *error = NULL; + struct tm *timeinfo; + time_t now; + FILE *fout; + char *rss_path = NULL, *pub_date = NULL; + char *rss_format = + "<rss version=\"2.0\"\n" + "<channel>\n" + "<title>%s</title>\n" + "<link>%s</link>\n" + "<lastBuildDate>%s</lastBuildDate>\n" + "%s\n" + "</channel>\n" + "</rss>\n"; + + time(&now); + timeinfo = localtime(&now); + pub_date = malloc(LONG_DATE_Z); + if (strftime(pub_date, LONG_DATE_Z, "%a, %d %b %Y %H:%M:%S %z", timeinfo) == 0) + goto out; + + if ((rss_path = build_full_path(dst_path, "rss.xml")) == NULL) + goto out; + + if ((fout = fopen(rss_path, "w")) == NULL) { + error = ssnail_error_from_errno("openw rss"); + goto out; + } + + int ret = fprintf(fout, rss_format, title, url, pub_date, rss_listing); + if (ret < 0) { + error = ssnail_error_from_errno("write rss"); + goto out; + } + + if (fclose(fout) != 0) + error = ssnail_error_from_errno("close fout"); + +out: + return error; +}