sfeed

Simple RSS and Atom feed parser
git clone https://git.sinitax.com/codemadness/sfeed
Log | Files | Refs | README | LICENSE | Upstream | sfeed.txt

commit 009f6e1affd8c8501f66526f100bfa273307e640
parent 018278e40b6794b23f98f57f154bfa1a30d56672
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Sat, 18 Jan 2020 19:19:41 +0100

add sfeed_gopher: generic gopher formatting program, remove sfeed_gph

Diffstat:
MMakefile | 2+-
MREADME | 2+-
Asfeed_gopher.1 | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asfeed_gopher.c | 159+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsfeed_gph.1 | 45---------------------------------------------
Dsfeed_gph.c | 152-------------------------------------------------------------------------------
6 files changed, 220 insertions(+), 199 deletions(-)

diff --git a/Makefile b/Makefile @@ -19,7 +19,7 @@ BIN = \ sfeed\ sfeed_atom\ sfeed_frames\ - sfeed_gph \ + sfeed_gopher \ sfeed_html\ sfeed_mbox\ sfeed_opml_import\ diff --git a/README b/README @@ -127,7 +127,7 @@ sfeed - Read XML RSS or Atom feed data from stdin. Write feed data in TAB-separated format to stdout. sfeed_atom - Format feed data (TSV) to an Atom feed. sfeed_frames - Format feed data (TSV) to HTML file(s) with frames. -sfeed_gph - Format feed data (TSV) to geomyidae .gph files. +sfeed_gopher - Format feed data (TSV) to Gopher files. sfeed_html - Format feed data (TSV) to HTML. sfeed_opml_export - Generate an OPML XML file from a sfeedrc config file. sfeed_opml_import - Generate a sfeedrc config file from an OPML XML file. diff --git a/sfeed_gopher.1 b/sfeed_gopher.1 @@ -0,0 +1,59 @@ +.Dd January 18, 2020 +.Dt SFEED_GOPHER 1 +.Os +.Sh NAME +.Nm sfeed_gopher +.Nd format feed data to Gopher files +.Sh SYNOPSIS +.Nm +.Op Ar file... +.Sh DESCRIPTION +.Nm +formats feed data (TSV) from +.Xr sfeed 1 +from stdin or +.Ar file +to stdout in the raw Gopher output format. +.Pp +If one or more +.Ar file +are specified, the basename of the +.Ar file +is used as the feed name in the output. +.Nm +creates a index index file and for each feed creates a file in the +format feedname. +.Pp +If no +.Ar file +parameters are specified and the data is read from stdin then the data is +written to stdout and no files are written. +.Pp +Items with a timestamp from the last day compared to the system time at the +time of formatting are counted and marked as new. +.Bl -tag -width Ds +.It Ev SFEED_GOPHER_PATH +This environment variable can be used as the prefix for each path in the +index file. +The default is "/". +.It Ev SFEED_GOPHER_HOST +This environment variable can be used as the Gopher Host field. +The default is "127.0.0.1". +.It Ev SFEED_GOPHER_PORT +This environment variable can be used as the Gopher Port field. +The default is "70". +.El +.Sh SEE ALSO +.Xr sfeed 1 , +.Xr sfeed 5 +.Sh STANDARDS +.Rs +.%D March 1993 +.%R RFC 1436 +.%T The Internet Gopher Protocol +.Re +.Sh AUTHORS +.An Hiltjo Posthuma Aq Mt hiltjo@codemadness.org +.Sh CAVEATS +The common-used 'h' and 'i' types are used in this program. +These types are an extension to Gopher. diff --git a/sfeed_gopher.c b/sfeed_gopher.c @@ -0,0 +1,159 @@ +#include <sys/stat.h> +#include <sys/types.h> + +#include <ctype.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 "util.h" + +static struct feed f; +static char *prefixpath = "/", *host = "127.0.0.1", *port = "70"; /* default */ +static char *line; +static size_t linesize; +static time_t comparetime; + +/* Escape characters in gopher, CR and LF are ignored */ +void +gophertext(FILE *fp, const char *s) +{ + for (; *s; s++) { + switch (*s) { + case '\r': /* ignore CR */ + case '\n': /* ignore LF */ + break; + case '\t': + fputs(" ", fp); + break; + default: + fputc(*s, fp); + break; + } + } +} + +static void +printfeed(FILE *fpitems, FILE *fpin, struct feed *f) +{ + char *fields[FieldLast]; + ssize_t linelen; + unsigned int isnew; + struct tm *tm; + time_t parsedtime; + + if (f->name[0]) { + fprintf(fpitems, "i%s\t\t%s\t%s\r\n", f->name, host, port); + fprintf(fpitems, "i\t\t%s\t%s\r\n", host, port); + } + + while ((linelen = getline(&line, &linesize, fpin)) > 0) { + if (line[linelen - 1] == '\n') + line[--linelen] = '\0'; + if (!parseline(line, fields)) + break; + + parsedtime = 0; + if (strtotime(fields[FieldUnixTimestamp], &parsedtime)) + continue; + if (!(tm = localtime(&parsedtime))) + err(1, "localtime"); + + isnew = (parsedtime >= comparetime) ? 1 : 0; + f->totalnew += isnew; + f->total++; + + if (fields[FieldLink][0]) { + fputs("h", fpitems); + fprintf(fpitems, "%c %04d-%02d-%02d %02d:%02d ", + isnew ? 'N' : ' ', + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min); + gophertext(fpitems, fields[FieldTitle]); + fputs("\tURL:", fpitems); + gophertext(fpitems, fields[FieldLink]); + + } else { + fprintf(fpitems, "i%c %04d-%02d-%02d %02d:%02d ", + isnew ? 'N' : ' ', + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min); + gophertext(fpitems, fields[FieldTitle]); + fputs("\t", fpitems); + } + fprintf(fpitems, "\t%s\t%s\r\n", host, port); + } + fputs(".\r\n", fpitems); +} + +int +main(int argc, char *argv[]) +{ + FILE *fpitems, *fpindex, *fp; + char *name, *p, path[PATH_MAX + 1]; + int i, r; + + if (pledge(argc == 1 ? "stdio" : "stdio rpath wpath cpath", NULL) == -1) + err(1, "pledge"); + + if ((comparetime = time(NULL)) == -1) + err(1, "time"); + /* 1 day is old news */ + comparetime -= 86400; + + if ((p = getenv("SFEED_GOPHER_HOST"))) + host = p; + if ((p = getenv("SFEED_GOPHER_PORT"))) + port = p; + + if (argc == 1) { + f.name = ""; + printfeed(stdout, stdin, &f); + } else { + if ((p = getenv("SFEED_GOPHER_PATH"))) + prefixpath = p; + + /* write main index page */ + if (!(fpindex = fopen("index", "wb"))) + err(1, "fopen: index"); + + for (i = 1; i < argc; i++) { + memset(&f, 0, sizeof(f)); + name = ((name = strrchr(argv[i], '/'))) ? name + 1 : argv[i]; + f.name = name; + + if (!(fp = fopen(argv[i], "r"))) + err(1, "fopen: %s", argv[i]); + + r = snprintf(path, sizeof(path), "%s", name); + if (r < 0 || (size_t)r >= sizeof(path)) + errx(1, "path truncation: %s", path); + if (!(fpitems = fopen(path, "wb"))) + err(1, "fopen"); + printfeed(fpitems, fp, &f); + if (ferror(fp)) + err(1, "ferror: %s", argv[i]); + fclose(fp); + fclose(fpitems); + + /* append directory item to index */ + fputs("1", fpindex); + gophertext(fpindex, name); + fprintf(fpindex, " (%lu/%lu)\t", f.totalnew, f.total); + gophertext(fpindex, prefixpath); + gophertext(fpindex, path); + fprintf(fpindex, "\t%s\t%s\r\n", host, port); + } + fputs(".\r\n", fpindex); + fclose(fpindex); + } + + return 0; +} diff --git a/sfeed_gph.1 b/sfeed_gph.1 @@ -1,45 +0,0 @@ -.Dd July 20, 2019 -.Dt SFEED_GPH 1 -.Os -.Sh NAME -.Nm sfeed_gph -.Nd format feed data to geomyidae .gph files -.Sh SYNOPSIS -.Nm -.Op Ar file... -.Sh DESCRIPTION -.Nm -formats feed data (TSV) from -.Xr sfeed 1 -from stdin or -.Ar file -to stdout in the geomyidae .gph file format. -.Pp -If one or more -.Ar file -are specified, the basename of the -.Ar file -is used as the feed name in the output. -.Nm -creates a index.gph index file and for each feed creates a file in the -format feedname.gph. -.Pp -If no -.Ar file -parameters are specified and the data is read from stdin then the gph data -is written to stdout and no files are written. -.Pp -Items with a timestamp from the last day compared to the system time at the -time of formatting are counted and marked as new. -.Bl -tag -width Ds -.It Ev SFEED_GPH_PATH -This environment variable can be used as the prefix for each path in the -index.gph file. -The default is "/". -.El -.Sh SEE ALSO -.Xr sfeed 1 , -.Xr sfeed 5 , -.Xr geomyidae 8 -.Sh AUTHORS -.An Hiltjo Posthuma Aq Mt hiltjo@codemadness.org diff --git a/sfeed_gph.c b/sfeed_gph.c @@ -1,152 +0,0 @@ -#include <sys/stat.h> -#include <sys/types.h> - -#include <ctype.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 "util.h" - -static struct feed f; -static char *prefixpath; -static char *line; -static size_t linesize; -static time_t comparetime; - -/* Escape characters in links in geomyidae .gph format */ -void -gphlink(FILE *fp, const char *s, size_t len) -{ - size_t i; - - for (i = 0; *s && i < len; s++, i++) { - switch (*s) { - case '\r': /* ignore CR */ - case '\n': /* ignore LF */ - break; - case '\t': - fputs(" ", fp); - break; - case '|': /* escape separators */ - fputs("\\|", fp); - break; - default: - fputc(*s, fp); - break; - } - } -} - -static void -printfeed(FILE *fpitems, FILE *fpin, struct feed *f) -{ - char *fields[FieldLast]; - ssize_t linelen; - unsigned int isnew; - struct tm *tm; - time_t parsedtime; - - if (f->name[0]) - fprintf(fpitems, "t%s\n\n", f->name); - - while ((linelen = getline(&line, &linesize, fpin)) > 0) { - if (line[linelen - 1] == '\n') - line[--linelen] = '\0'; - if (!parseline(line, fields)) - break; - - parsedtime = 0; - if (strtotime(fields[FieldUnixTimestamp], &parsedtime)) - continue; - if (!(tm = localtime(&parsedtime))) - err(1, "localtime"); - - isnew = (parsedtime >= comparetime) ? 1 : 0; - f->totalnew += isnew; - f->total++; - - if (fields[FieldLink][0]) { - fputs("[h|", fpitems); - fprintf(fpitems, "%c %04d-%02d-%02d %02d:%02d ", - isnew ? 'N' : ' ', - tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, - tm->tm_hour, tm->tm_min); - gphlink(fpitems, fields[FieldTitle], strlen(fields[FieldTitle])); - fputs("|URL:", fpitems); - gphlink(fpitems, fields[FieldLink], strlen(fields[FieldLink])); - fputs("|server|port]\n", fpitems); - } else { - fprintf(fpitems, "%c %04d-%02d-%02d %02d:%02d %s\n", - isnew ? 'N' : ' ', - tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, - tm->tm_hour, tm->tm_min, fields[FieldTitle]); - } - } -} - -int -main(int argc, char *argv[]) -{ - FILE *fpitems, *fpindex, *fp; - char *name, path[PATH_MAX + 1]; - int i, r; - - if (pledge(argc == 1 ? "stdio" : "stdio rpath wpath cpath", NULL) == -1) - err(1, "pledge"); - - if ((comparetime = time(NULL)) == -1) - err(1, "time"); - /* 1 day is old news */ - comparetime -= 86400; - - if (argc == 1) { - f.name = ""; - printfeed(stdout, stdin, &f); - } else { - if (!(prefixpath = getenv("SFEED_GPH_PATH"))) - prefixpath = "/"; - - /* write main index page */ - if (!(fpindex = fopen("index.gph", "wb"))) - err(1, "fopen: index.gph"); - - for (i = 1; i < argc; i++) { - memset(&f, 0, sizeof(f)); - name = ((name = strrchr(argv[i], '/'))) ? name + 1 : argv[i]; - f.name = name; - - if (!(fp = fopen(argv[i], "r"))) - err(1, "fopen: %s", argv[i]); - - r = snprintf(path, sizeof(path), "%s.gph", name); - if (r < 0 || (size_t)r >= sizeof(path)) - errx(1, "path truncation: %s", path); - if (!(fpitems = fopen(path, "wb"))) - err(1, "fopen"); - printfeed(fpitems, fp, &f); - if (ferror(fp)) - err(1, "ferror: %s", argv[i]); - fclose(fp); - fclose(fpitems); - - /* append directory item to index */ - fprintf(fpindex, "[1|"); - gphlink(fpindex, name, strlen(name)); - fprintf(fpindex, " (%lu/%lu)|", f.totalnew, f.total); - gphlink(fpindex, prefixpath, strlen(prefixpath)); - gphlink(fpindex, path, strlen(path)); - fputs("|server|port]\n", fpindex); - } - fclose(fpindex); - } - - return 0; -}