sfeed_frames_content: add old archived sfeed_frames version with content - randomcrap - random crap programs of varying quality
HTML git clone git://git.codemadness.org/randomcrap
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
DIR commit b952e0706644f6ae41c607afabab59e4a44f7580
DIR parent 941ecd1ec38a5d0b4e7b4f3ed3e9ea6234b6ed8b
HTML Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date: Sat, 18 Mar 2023 19:56:56 +0100
sfeed_frames_content: add old archived sfeed_frames version with content
This was a version of sfeed_frames which saved the HTML content per file.
Backport some recent changes of the format tools (localtime_r, unveil,
parseline, etc).
From sfeed commit b7e288a96418e1ea5e7904ab2896edb3f4615a10:
Date: Thu Aug 16 14:16:58 2018 +0200
sfeed_frames: overhaul
Diffstat:
A sfeed/sfeed_frames_content.c | 345 +++++++++++++++++++++++++++++++
1 file changed, 345 insertions(+), 0 deletions(-)
---
DIR diff --git a/sfeed/sfeed_frames_content.c b/sfeed/sfeed_frames_content.c
@@ -0,0 +1,345 @@
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <utime.h>
+
+#include "util.h"
+
+static struct feed **feeds;
+static char *line;
+static size_t linesize;
+static struct timespec times[2];
+static time_t comparetime;
+static unsigned long totalnew, total;
+
+/* Unescape / decode fields printed by string_print_encoded()
+ * "\\" to "\", "\t", to TAB, "\n" to newline. Unrecognised escape sequences
+ * are ignored: "\z" etc. */
+static void
+printcontent(const char *s, FILE *fp)
+{
+ for (; *s; s++) {
+ switch (*s) {
+ case '\\':
+ switch (*(++s)) {
+ case '\0': return; /* ignore */
+ case '\\': putc('\\', fp); break;
+ case 't': putc('\t', fp); break;
+ case 'n': putc('\n', fp); break;
+ }
+ break;
+ default:
+ putc(*s, fp);
+ }
+ }
+}
+
+/* Unescape / decode fields printed by string_print_encoded()
+ * "\\" to "\", "\t", to TAB, "\n" to newline. Unrecognised escape sequences
+ * are ignored: "\z" etc. Encode HTML 2.0 / XML 1.0 entities. */
+static void
+printcontentxml(const char *s, FILE *fp)
+{
+ for (; *s; s++) {
+ switch (*s) {
+ case '\\':
+ switch (*(++s)) {
+ case '\0': return; /* ignore */
+ case '\\': putc('\\', fp); break;
+ case 't': putc('\t', fp); break;
+ case 'n': putc('\n', fp); break;
+ }
+ break;
+ /* XML entities */
+ case '<': fputs("<", fp); break;
+ case '>': fputs(">", fp); break;
+ case '\'': fputs("'", fp); break;
+ case '&': fputs("&", fp); break;
+ case '"': fputs(""", fp); break;
+ default: putc(*s, fp);
+ }
+ }
+}
+
+/* normalize path names, transform to lower-case and replace non-alpha and
+ * non-digit with '-' */
+static size_t
+normalizepath(const char *path, char *buf, size_t bufsiz)
+{
+ size_t i, r = 0;
+
+ for (i = 0; *path && i < bufsiz - 1; path++) {
+ if (ISALPHA((unsigned char)*path) || ISDIGIT((unsigned char)*path)) {
+ buf[i++] = TOLOWER((unsigned char)*path);
+ r = 0;
+ } else {
+ /* don't repeat '-', don't start with '-' */
+ if (!r && i)
+ buf[i++] = '-';
+ r++;
+ }
+ }
+ /* remove trailing '-' */
+ for (; i > 0 && (buf[i - 1] == '-'); i--)
+ ;
+
+ buf[i] = '\0';
+
+ return i;
+}
+
+static void
+printfeed(FILE *fpitems, FILE *fpin, struct feed *f)
+{
+ char dirpath[PATH_MAX], filepath[PATH_MAX];
+ char *fields[FieldLast], *feedname, name[128];
+ ssize_t linelen;
+ FILE *fpcontent = NULL;
+ unsigned int isnew;
+ struct tm rtm, *tm;
+ time_t parsedtime;
+ int fd, r;
+
+ if (f->name[0])
+ feedname = f->name;
+ else
+ feedname = "unnamed";
+
+ /* make directory for feedname */
+ if (!normalizepath(feedname, name, sizeof(name)))
+ return;
+
+ strlcpy(dirpath, name, sizeof(dirpath));
+
+ /* error creating directory and it doesn't exist. */
+ if (mkdir(dirpath, S_IRWXU | S_IRWXG | S_IRWXO) == -1 && errno != EEXIST)
+ err(1, "mkdir: %s", dirpath);
+
+ /* menu if not unnamed */
+ if (f->name[0]) {
+ fputs("<h2 id=\"", fpitems);
+ xmlencode(f->name, fpitems);
+ fputs("\"><a href=\"#", fpitems);
+ xmlencode(f->name, fpitems);
+ fputs("\">", fpitems);
+ xmlencode(f->name, fpitems);
+ fputs("</a></h2>\n", fpitems);
+ }
+
+ while ((linelen = getline(&line, &linesize, fpin)) > 0) {
+ if (line[linelen - 1] == '\n')
+ line[--linelen] = '\0';
+ parseline(line, fields);
+
+ parsedtime = 0;
+ if (strtotime(fields[FieldUnixTimestamp], &parsedtime))
+ continue;
+ if (!(tm = localtime_r(&parsedtime, &rtm)))
+ err(1, "localtime");
+
+ if (!normalizepath(fields[FieldTitle], name, sizeof(name)))
+ continue;
+
+ r = snprintf(filepath, sizeof(filepath), "%s/%s-%lld.html",
+ dirpath, name, (long long)parsedtime);
+ if (r == -1 || (size_t)r >= sizeof(filepath))
+ errx(1, "snprintf: path truncation: '%s/%s-%lld.html'",
+ dirpath, name, (long long)parsedtime);
+
+ /* content file doesn't exist yet and has error? */
+ if ((fd = open(filepath, O_CREAT | O_EXCL | O_WRONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) == -1) {
+ if (errno != EEXIST)
+ err(1, "open: %s", filepath);
+ } else {
+ if (!(fpcontent = fdopen(fd, "wb")))
+ err(1, "fdopen: %s", filepath);
+ fputs("<html><head>"
+ "<link rel=\"stylesheet\" type=\"text/css\" href=\"../../style.css\" />"
+ "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />"
+ "</head>\n<body class=\"frame\">"
+ "<div class=\"content\"><h2>", fpcontent);
+
+ if (fields[FieldLink][0]) {
+ fputs("<a href=\"", fpcontent);
+ xmlencode(fields[FieldLink], fpcontent);
+ fputs("\">", fpcontent);
+ }
+ xmlencode(fields[FieldTitle], fpcontent);
+ if (fields[FieldLink][0])
+ fputs("</a>", fpcontent);
+ fputs("</h2>", fpcontent);
+
+ /* NOTE: this prints the raw HTML of the feed, this is
+ * potentially dangerous, it is left up to the
+ * user / browser to trust a feed it's HTML content. */
+ if (!strcmp(fields[FieldContentType], "html")) {
+ printcontent(fields[FieldContent], fpcontent);
+ } else {
+ /* plain-text, wrap with <pre> */
+ fputs("<pre>", fpcontent);
+ printcontentxml(fields[FieldContent], fpcontent);
+ fputs("</pre>", fpcontent);
+ }
+ fputs("</div></body></html>\n", fpcontent);
+
+ /* set modified and access time of file to time of item. */
+ if (parsedtime) {
+ /* flush writes before setting atime and mtime
+ else the remaining (buffered) write can occur at
+ fclose() and overwrite our time again. */
+ fflush(fpcontent);
+
+ times[0].tv_sec = parsedtime;
+ times[1].tv_sec = parsedtime;
+
+ if (futimens(fd, times) == -1)
+ err(1, "futimens");
+ }
+ fclose(fpcontent);
+ }
+
+ isnew = (parsedtime >= comparetime) ? 1 : 0;
+ totalnew += isnew;
+ f->totalnew += isnew;
+ f->total++;
+ total++;
+
+ fprintf(fpitems, "%04d-%02d-%02d %02d:%02d ",
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min);
+
+ if (isnew)
+ fputs("<b><u>", fpitems);
+ fputs("<a href=\"", fpitems);
+ fputs(filepath, fpitems);
+ fputs("\" target=\"content\">", fpitems);
+ xmlencode(fields[FieldTitle], fpitems);
+ fputs("</a>", fpitems);
+ if (isnew)
+ fputs("</u></b>", fpitems);
+ fputs("\n", fpitems);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ FILE *fpindex, *fpitems, *fpmenu = NULL, *fp;
+ char *name;
+ int i, showsidebar = (argc > 1);
+ struct feed *f;
+
+ if (unveil("/", "r") == -1)
+ err(1, "unveil: /");
+ if (unveil(".", "rwc") == -1)
+ err(1, "unveil: .");
+ if (pledge("stdio rpath wpath cpath fattr", NULL) == -1)
+ err(1, "pledge");
+
+ if (!(feeds = calloc(argc, sizeof(struct feed *))))
+ err(1, "calloc");
+
+ if ((comparetime = time(NULL)) == -1)
+ errx(1, "time");
+ /* 1 day is old news */
+ comparetime -= 86400;
+
+ /* write main index page */
+ if (!(fpindex = fopen("index.html", "wb")))
+ err(1, "fopen: index.html");
+ if (!(fpitems = fopen("items.html", "wb")))
+ err(1, "fopen: items.html");
+ if (showsidebar && !(fpmenu = fopen("menu.html", "wb")))
+ err(1, "fopen: menu.html");
+ fputs("<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"../style.css\" />"
+ "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" /></head>"
+ "<body class=\"frame\"><div id=\"items\"><pre>", fpitems);
+
+ if (argc == 1) {
+ if (!(feeds[0] = calloc(1, sizeof(struct feed))))
+ err(1, "calloc");
+ feeds[0]->name = "";
+ printfeed(fpitems, stdin, feeds[0]);
+ checkfileerror(stdin, "<stdin>", 'r');
+ } else {
+ for (i = 1; i < argc; i++) {
+ if (!(feeds[i - 1] = calloc(1, sizeof(struct feed))))
+ err(1, "calloc");
+ name = ((name = strrchr(argv[i], '/'))) ? name + 1 : argv[i];
+ feeds[i - 1]->name = name;
+
+ if (!(fp = fopen(argv[i], "r")))
+ err(1, "fopen: %s", argv[i]);
+ printfeed(fpitems, fp, feeds[i - 1]);
+ checkfileerror(fp, argv[i], 'r');
+ checkfileerror(fpitems, "items.html", 'w');
+ fclose(fp);
+ }
+ }
+ fputs("</pre>\n</div></body>\n</html>\n", fpitems); /* div items */
+
+ if (showsidebar) {
+ fputs("<html><head>"
+ "<link rel=\"stylesheet\" type=\"text/css\" href=\"../style.css\" />\n"
+ "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
+ "</head><body class=\"frame\"><div id=\"sidebar\">", fpmenu);
+
+ for (i = 1; i < argc; i++) {
+ f = feeds[i - 1];
+ if (f->totalnew)
+ fputs("<a class=\"n\" href=\"items.html#", fpmenu);
+ else
+ fputs("<a href=\"items.html#", fpmenu);
+ xmlencode(f->name, fpmenu);
+ fputs("\" target=\"items\">", fpmenu);
+ if (f->totalnew > 0)
+ fputs("<b><u>", fpmenu);
+ xmlencode(f->name, fpmenu);
+ fprintf(fpmenu, " (%lu)", f->totalnew);
+ if (f->totalnew > 0)
+ fputs("</u></b>", fpmenu);
+ fputs("</a><br/>\n", fpmenu);
+ }
+ fputs("</div></body></html>\n", fpmenu);
+ }
+ fputs("<!DOCTYPE html><html><head>\n\t<title>(", fpindex);
+ fprintf(fpindex, "%lu/%lu", totalnew, total);
+ fputs(") - Newsfeed</title>\n\t<link rel=\"stylesheet\" type=\"text/css\" href=\"../style.css\" />\n"
+ "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
+ "</head>\n", fpindex);
+ if (showsidebar) {
+ fputs("<frameset framespacing=\"0\" cols=\"200,*\" frameborder=\"1\">\n"
+ " <frame name=\"menu\" src=\"menu.html\" target=\"menu\">\n", fpindex);
+ } else {
+ fputs("<frameset framespacing=\"0\" cols=\"*\" frameborder=\"1\">\n", fpindex);
+ }
+ fputs("\t<frameset id=\"frameset\" framespacing=\"0\" cols=\"50%,50%\" frameborder=\"1\">\n"
+ "\t\t<frame name=\"items\" src=\"items.html\" target=\"items\">\n"
+ "\t\t<frame name=\"content\" target=\"content\">\n"
+ "\t</frameset>\n"
+ "</frameset>\n"
+ "</html>\n", fpindex);
+
+ checkfileerror(fpindex, "index.html", 'w');
+ checkfileerror(fpitems, "items.html", 'w');
+
+ fclose(fpitems);
+ fclose(fpindex);
+ if (fpmenu) {
+ checkfileerror(fpmenu, "menu.html", 'w');
+ fclose(fpmenu);
+ }
+
+ return 0;
+}