sfeed

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

sfeed_atom.c (4200B)


      1#include <stdio.h>
      2#include <string.h>
      3#include <time.h>
      4
      5#include "util.h"
      6
      7static struct tm tmnow;
      8static time_t now;
      9static char *line;
     10static size_t linesize;
     11
     12static void
     13printcontent(const char *s)
     14{
     15	for (; *s; ++s) {
     16		switch (*s) {
     17		case '<':  fputs("&lt;",   stdout); break;
     18		case '>':  fputs("&gt;",   stdout); break;
     19		case '\'': fputs("&#39;",  stdout); break;
     20		case '&':  fputs("&amp;",  stdout); break;
     21		case '"':  fputs("&quot;", stdout); break;
     22		case '\\':
     23			if (*(s + 1) == '\0')
     24				break;
     25			s++;
     26			switch (*s) {
     27			case 'n':  putchar('\n'); break;
     28			case '\\': putchar('\\'); break;
     29			case 't':  putchar('\t'); break;
     30			}
     31			break;
     32		default:  putchar(*s);
     33		}
     34	}
     35}
     36
     37static void
     38printfeed(FILE *fp, const char *feedname)
     39{
     40	char *fields[FieldLast], *p, *tmp;
     41	struct tm parsedtm, *tm;
     42	time_t parsedtime;
     43	ssize_t linelen;
     44	int c;
     45
     46	while ((linelen = getline(&line, &linesize, fp)) > 0 &&
     47	       !ferror(stdout)) {
     48		if (line[linelen - 1] == '\n')
     49			line[--linelen] = '\0';
     50		parseline(line, fields);
     51
     52		fputs("<entry>\n\t<title>", stdout);
     53		if (feedname[0]) {
     54			fputs("[", stdout);
     55			xmlencode(feedname, stdout);
     56			fputs("] ", stdout);
     57		}
     58		xmlencode(fields[FieldTitle], stdout);
     59		fputs("</title>\n", stdout);
     60		if (fields[FieldLink][0]) {
     61			fputs("\t<link rel=\"alternate\" href=\"", stdout);
     62			xmlencode(fields[FieldLink], stdout);
     63			fputs("\" />\n", stdout);
     64		}
     65		/* prefer link over id for Atom <id>. */
     66		fputs("\t<id>", stdout);
     67		if (fields[FieldLink][0])
     68			xmlencode(fields[FieldLink], stdout);
     69		else if (fields[FieldId][0])
     70			xmlencode(fields[FieldId], stdout);
     71		fputs("</id>\n", stdout);
     72		if (fields[FieldEnclosure][0]) {
     73			fputs("\t<link rel=\"enclosure\" href=\"", stdout);
     74			xmlencode(fields[FieldEnclosure], stdout);
     75			fputs("\" />\n", stdout);
     76		}
     77
     78		parsedtime = 0;
     79		if (strtotime(fields[FieldUnixTimestamp], &parsedtime) ||
     80		    !(tm = gmtime_r(&parsedtime, &parsedtm)))
     81			tm = &tmnow;
     82		fprintf(stdout, "\t<updated>%04d-%02d-%02dT%02d:%02d:%02dZ</updated>\n",
     83		        tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
     84		        tm->tm_hour, tm->tm_min, tm->tm_sec);
     85
     86		if (fields[FieldAuthor][0]) {
     87			fputs("\t<author><name>", stdout);
     88			xmlencode(fields[FieldAuthor], stdout);
     89			fputs("</name></author>\n", stdout);
     90		}
     91		if (fields[FieldContent][0]) {
     92			if (!strcmp(fields[FieldContentType], "html")) {
     93				fputs("\t<content type=\"html\">", stdout);
     94			} else {
     95				/* NOTE: an RSS/Atom viewer may or may not format
     96				   whitespace such as newlines.
     97				   Workaround: type="html" and <![CDATA[<pre></pre>]]> */
     98				fputs("\t<content>", stdout);
     99			}
    100			printcontent(fields[FieldContent]);
    101			fputs("</content>\n", stdout);
    102		}
    103		for (p = fields[FieldCategory]; (tmp = strchr(p, '|')); p = tmp + 1) {
    104			c = *tmp;
    105			*tmp = '\0'; /* temporary NUL-terminate */
    106			if (*p) {
    107				fputs("\t<category term=\"", stdout);
    108				xmlencode(p, stdout);
    109				fputs("\" />\n", stdout);
    110			}
    111			*tmp = c; /* restore */
    112		}
    113		fputs("</entry>\n", stdout);
    114	}
    115}
    116
    117int
    118main(int argc, char *argv[])
    119{
    120	struct tm *tm;
    121	FILE *fp;
    122	char *name;
    123	int i;
    124
    125	if (pledge(argc == 1 ? "stdio" : "stdio rpath", NULL) == -1)
    126		err(1, "pledge");
    127
    128	if ((now = time(NULL)) == (time_t)-1)
    129		errx(1, "time");
    130	if (!(tm = gmtime_r(&now, &tmnow)))
    131		err(1, "gmtime_r: can't get current time");
    132
    133	fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
    134	      "<feed xmlns=\"http://www.w3.org/2005/Atom\">\n"
    135	      "\t<title>Newsfeed</title>\n", stdout);
    136	printf("\t<id>urn:newsfeed:%lld</id>\n"
    137	       "\t<updated>%04d-%02d-%02dT%02d:%02d:%02dZ</updated>\n",
    138	       (long long)now,
    139	       tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
    140	       tm->tm_hour, tm->tm_min, tm->tm_sec);
    141
    142	if (argc == 1) {
    143		printfeed(stdin, "");
    144		checkfileerror(stdin, "<stdin>", 'r');
    145	} else {
    146		for (i = 1; i < argc; i++) {
    147			if (!(fp = fopen(argv[i], "r")))
    148				err(1, "fopen: %s", argv[i]);
    149			name = ((name = strrchr(argv[i], '/'))) ? name + 1 : argv[i];
    150			printfeed(fp, name);
    151			checkfileerror(fp, argv[i], 'r');
    152			checkfileerror(stdout, "<stdout>", 'w');
    153			fclose(fp);
    154		}
    155	}
    156
    157	fputs("</feed>\n", stdout);
    158
    159	checkfileerror(stdout, "<stdout>", 'w');
    160
    161	return 0;
    162}