Introduction
Introduction Statistics Contact Development Disclaimer Help
sfeed_json: add JSON output format tool - sfeed - RSS and Atom parser
git clone git://git.codemadness.org/sfeed
Log
Files
Refs
README
LICENSE
---
commit ced0dd7f8a01dedad3ba16c4cf209aea673c3e82
parent 3f1d323497e5aefce65a3758d1bc408130d9bbe0
Author: Hiltjo Posthuma <[email protected]>
Date: Mon, 10 Apr 2023 17:03:57 +0200
sfeed_json: add JSON output format tool
This outputs the TSV data to JSON.
It uses a subset of JSON Feed 1.1:
https://www.jsonfeed.org/version/1.1/
Diffstat:
M Makefile | 1 +
M README | 1 +
A sfeed_json.1 | 48 +++++++++++++++++++++++++++++…
A sfeed_json.c | 171 +++++++++++++++++++++++++++++…
4 files changed, 221 insertions(+), 0 deletions(-)
---
diff --git a/Makefile b/Makefile
@@ -51,6 +51,7 @@ BIN = \
sfeed_frames\
sfeed_gopher\
sfeed_html\
+ sfeed_json\
sfeed_mbox\
sfeed_opml_import\
sfeed_plain\
diff --git a/README b/README
@@ -186,6 +186,7 @@ sfeed_curses - Format feed data (TSV) to a curses inte…
sfeed_frames - Format feed data (TSV) to HTML file(s) with frames.
sfeed_gopher - Format feed data (TSV) to Gopher files.
sfeed_html - Format feed data (TSV) to HTML.
+sfeed_json - Format feed data (TSV) to JSON Feed.
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.
sfeed_markread - Mark items as read/unread, for use with sfeed_curses.
diff --git a/sfeed_json.1 b/sfeed_json.1
@@ -0,0 +1,48 @@
+.Dd March 8, 2023
+.Dt SFEED_JSON 1
+.Os
+.Sh NAME
+.Nm sfeed_json
+.Nd format feed data to JSON Feed
+.Sh SYNOPSIS
+.Nm
+.Op Ar
+.Sh DESCRIPTION
+.Nm
+formats feed data (TSV) from
+.Xr sfeed 1
+from stdin or for each
+.Ar file
+to stdout as JSON Feed data.
+If one or more
+.Ar file
+arguments are specified then the basename of the
+.Ar file
+is used as the feed name in the output.
+If no
+.Ar file
+arguments are specified and so the data is read from stdin then the feed name
+is empty.
+If
+.Nm
+is reading from one or more
+.Ar file
+arguments it will prefix the entry title with "[feed name] ".
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+.Bd -literal
+curl -s 'https://codemadness.org/atom.xml' | sfeed | sfeed_json
+.Ed
+.Sh SEE ALSO
+.Xr sfeed 1 ,
+.Xr sfeed_atom 1 ,
+.Xr sfeed 5
+.Sh STANDARDS
+.Rs
+.%T JSON Feed Version 1.1
+.%U https://www.jsonfeed.org/version/1.1/
+.%D Nov, 2022
+.Re
+.Sh AUTHORS
+.An Hiltjo Posthuma Aq Mt [email protected]
diff --git a/sfeed_json.c b/sfeed_json.c
@@ -0,0 +1,171 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+
+static char *line;
+static size_t linesize;
+static int firstitem = 1;
+
+/* Unescape / decode fields printed by string_print_encoded() */
+static void
+printcontent(const char *s)
+{
+ for (; *s; s++) {
+ switch (*s) {
+ case '\\':
+ s++;
+ switch (*s) {
+ case 'n': fputs("\\n", stdout); break;
+ case '\\': fputs("\\\\", stdout); break;
+ case 't': fputs("\\t", stdout); break;
+ }
+ break; /* ignore invalid escape sequence */
+ case '"': fputs("\\\"", stdout); break;
+ default:
+ putchar(*s);
+ break;
+ }
+ }
+}
+
+static void
+printfield(const char *s)
+{
+ for (; *s; s++) {
+ if (*s == '\\')
+ fputs("\\\\", stdout);
+ else if (*s == '"')
+ fputs("\\\"", stdout);
+ else
+ putchar(*s);
+ }
+}
+
+static void
+printfeed(FILE *fp, const char *feedname)
+{
+ char *fields[FieldLast], timebuf[32];
+ struct tm parsedtm, *tm;
+ time_t parsedtime;
+ ssize_t linelen;
+ int ch;
+ char *p, *s;
+
+ while ((linelen = getline(&line, &linesize, fp)) > 0 &&
+ !ferror(stdout)) {
+ if (line[linelen - 1] == '\n')
+ line[--linelen] = '\0';
+ parseline(line, fields);
+
+ if (!firstitem)
+ fputs(",\n", stdout);
+ firstitem = 0;
+
+ fputs("{\n\t\"id\": \"", stdout);
+ printfield(fields[FieldId]);
+ fputs("\"", stdout);
+
+ parsedtime = 0;
+ if (!strtotime(fields[FieldUnixTimestamp], &parsedtime) &&
+ (tm = gmtime_r(&parsedtime, &parsedtm)) &&
+ strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%SZ", t…
+ fputs(",\n\t\"date_published\": \"", stdout);
+ fputs(timebuf, stdout);
+ fputs("\"", stdout);
+ }
+
+ fputs(",\n\t\"title\": \"", stdout);
+ if (feedname[0]) {
+ fputs("[", stdout);
+ printfield(feedname);
+ fputs("] ", stdout);
+ }
+ printfield(fields[FieldTitle]);
+ fputs("\"", stdout);
+
+ if (fields[FieldLink][0]) {
+ fputs(",\n\t\"url\": \"", stdout);
+ printfield(fields[FieldLink]);
+ fputs("\"", stdout);
+ }
+
+ if (fields[FieldAuthor][0]) {
+ fputs(",\n\t\"authors\": [{\"name\": \"", stdout);
+ printfield(fields[FieldAuthor]);
+ fputs("\"}]", stdout);
+ }
+
+ if (fields[FieldCategory][0]) {
+ fputs(",\n\t\"tags\": [", stdout);
+
+ for (p = s = fields[FieldCategory]; ; s++) {
+ if (*s == '|' || *s == '\0') {
+ if (p != fields[FieldCategory])
+ fputs(", ", stdout);
+ ch = *s;
+ *s = '\0'; /* temporary NUL terminate …
+ fputs("\"", stdout);
+ printfield(p);
+ fputs("\"", stdout);
+ *s = ch; /* restore */
+ p = s + 1;
+ }
+ if (*s == '\0')
+ break;
+ }
+ fputs("]", stdout);
+ }
+
+ if (fields[FieldEnclosure][0]) {
+ fputs(",\n\t\"attachments\": [{\"url:\": \"", stdout);
+ printfield(fields[FieldEnclosure]);
+ fputs("\"}]", stdout);
+ }
+
+ if (!strcmp(fields[FieldContentType], "html"))
+ fputs(",\n\t\"content_html\": \"", stdout);
+ else
+ fputs(",\n\t\"content_text\": \"", stdout);
+ printcontent(fields[FieldContent]);
+ fputs("\"\n}", stdout);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ FILE *fp;
+ char *name;
+ int i;
+
+ if (pledge(argc == 1 ? "stdio" : "stdio rpath", NULL) == -1)
+ err(1, "pledge");
+
+ fputs("{\n"
+ "\"version\": \"https://jsonfeed.org/version/1.1\",\n"
+ "\"title\": \"Newsfeed\",\n"
+ "\"items\": [\n", stdout);
+
+ if (argc == 1) {
+ printfeed(stdin, "");
+ checkfileerror(stdin, "<stdin>", 'r');
+ } else {
+ for (i = 1; i < argc; i++) {
+ if (!(fp = fopen(argv[i], "r")))
+ err(1, "fopen: %s", argv[i]);
+ name = ((name = strrchr(argv[i], '/'))) ? name + 1 : a…
+ printfeed(fp, name);
+ checkfileerror(fp, argv[i], 'r');
+ checkfileerror(stdout, "<stdout>", 'w');
+ fclose(fp);
+ }
+ }
+ fputs("]\n}\n", stdout);
+
+ checkfileerror(stdout, "<stdout>", 'w');
+
+ return 0;
+}
You are viewing proxied material from codemadness.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.