sfeed_gopher.c - sfeed - RSS and Atom parser
(HTM) git clone git://git.codemadness.org/sfeed
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
sfeed_gopher.c (5752B)
---
1 #include <locale.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <time.h>
6
7 #include "util.h"
8
9 static char *prefixpath = "/", *host = "127.0.0.1", *port = "70"; /* default */
10 static char *line;
11 static size_t linesize;
12 static time_t comparetime;
13 static struct feed *feeds;
14
15 /* Escape characters in Gopher, CR and LF are ignored */
16 static void
17 gophertext(FILE *fp, const char *s)
18 {
19 for (; *s; s++) {
20 switch (*s) {
21 case '\r': /* ignore CR */
22 case '\n': /* ignore LF */
23 break;
24 case '\t':
25 fputs(" ", fp);
26 break;
27 default:
28 putc(*s, fp);
29 break;
30 }
31 }
32 }
33
34 static void
35 printfeed(FILE *fpitems, FILE *fpin, struct feed *f)
36 {
37 struct uri u;
38 char *fields[FieldLast];
39 char *itemhost, *itemport, *itempath, *itemquery, *itemfragment;
40 ssize_t linelen;
41 unsigned int isnew;
42 struct tm rtm, *tm;
43 time_t parsedtime;
44 int itemtype;
45
46 if (f->name[0]) {
47 fprintf(fpitems, "i%s\t\t%s\t%s\r\n", f->name, host, port);
48 fprintf(fpitems, "i\t\t%s\t%s\r\n", host, port);
49 }
50
51 while ((linelen = getline(&line, &linesize, fpin)) > 0 &&
52 !ferror(fpitems)) {
53 if (line[linelen - 1] == '\n')
54 line[--linelen] = '\0';
55 parseline(line, fields);
56
57 itemhost = host;
58 itemport = port;
59 itemtype = 'i'; /* i type (extension): informational message */
60 itempath = fields[FieldLink];
61 itemquery = "";
62 itemfragment = "";
63
64 if (fields[FieldLink][0]) {
65 itemtype = 'h'; /* h type (extension): HTML or external URL */
66 /* if it is a Gopher URL then change it into a DirEntity */
67 if (!strncmp(fields[FieldLink], "gopher://", 9) &&
68 uri_parse(fields[FieldLink], &u) != -1) {
69 itemhost = u.host;
70 itemport = u.port[0] ? u.port : "70";
71 itemtype = '1'; /* directory */
72 itempath = u.path;
73 itemquery = u.query;
74 itemfragment = u.fragment;
75 /* use the Gopher type from the path if it is set */
76 if (itempath[0] == '/') {
77 itempath++;
78 if (*itempath) {
79 itemtype = *itempath;
80 itempath++;
81 }
82 }
83 }
84 }
85
86 parsedtime = 0;
87 if (!strtotime(fields[FieldUnixTimestamp], &parsedtime) &&
88 (tm = localtime_r(&parsedtime, &rtm))) {
89 isnew = (parsedtime >= comparetime) ? 1 : 0;
90 f->totalnew += isnew;
91
92 fprintf(fpitems, "%c%c %04d-%02d-%02d %02d:%02d ",
93 itemtype,
94 isnew ? 'N' : ' ',
95 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
96 tm->tm_hour, tm->tm_min);
97 } else {
98 fprintf(fpitems, "%c ", itemtype);
99 }
100 f->total++;
101
102 gophertext(fpitems, fields[FieldTitle]);
103 fputs("\t", fpitems);
104 /* Prefix non-Gopher URLs with "URL:" using the h type.
105 This Gopher extension is commonly used to link to external URLs */
106 if (itemtype == 'h' && fields[FieldLink] == itempath) {
107 fputs("URL:", fpitems);
108 gophertext(fpitems, fields[FieldLink]);
109 } else {
110 gophertext(fpitems, itempath);
111 if (itemquery[0]) {
112 fputs("?", fpitems);
113 gophertext(fpitems, itemquery);
114 }
115 if (itemfragment[0]) {
116 fputs("#", fpitems);
117 gophertext(fpitems, itemfragment);
118 }
119 }
120 fprintf(fpitems, "\t%s\t%s\r\n", itemhost, itemport);
121 }
122 fputs(".\r\n", fpitems);
123 }
124
125 int
126 main(int argc, char *argv[])
127 {
128 struct feed *f;
129 FILE *fpitems, *fpindex, *fp;
130 char buf[64], *name, *p;
131 size_t maxcountlen = 0, maxnamelen = 0, len;
132 int i;
133
134 if (argc <= 1) {
135 if (pledge("stdio", NULL) == -1)
136 err(1, "pledge");
137 } else {
138 if (unveil("/", "r") == -1)
139 err(1, "unveil: /");
140 if (unveil(".", "rwc") == -1)
141 err(1, "unveil: .");
142 if (pledge("stdio rpath wpath cpath", NULL) == -1)
143 err(1, "pledge");
144
145 setlocale(LC_CTYPE, "");
146 }
147
148 if ((comparetime = getcomparetime()) == (time_t)-1)
149 errx(1, "getcomparetime");
150
151 if ((p = getenv("SFEED_GOPHER_HOST")))
152 host = p;
153 if ((p = getenv("SFEED_GOPHER_PORT")))
154 port = p;
155 if ((p = getenv("SFEED_GOPHER_PATH")))
156 prefixpath = p;
157
158 if (!(feeds = calloc(argc <= 1 ? 1 : argc, sizeof(struct feed))))
159 err(1, "calloc");
160
161 if (argc <= 1) {
162 feeds[0].name = "";
163 printfeed(stdout, stdin, &feeds[0]);
164 checkfileerror(stdin, "<stdin>", 'r');
165 checkfileerror(stdout, "<stdout>", 'w');
166 } else {
167 for (i = 1; i < argc; i++) {
168 name = ((name = strrchr(argv[i], '/'))) ? name + 1 : argv[i];
169 f = &feeds[i - 1];
170 f->name = name;
171
172 if (!(fp = fopen(argv[i], "r")))
173 err(1, "fopen: %s", argv[i]);
174 if (!(fpitems = fopen(name, "wb")))
175 err(1, "fopen");
176 printfeed(fpitems, fp, f);
177 checkfileerror(fp, argv[i], 'r');
178 checkfileerror(fpitems, name, 'w');
179 fclose(fp);
180 fclose(fpitems);
181
182 /* count max length: used for aligning the feed names */
183 len = colw(name);
184 if (len > maxnamelen)
185 maxnamelen = len;
186
187 /* count max length: used for aligning the feed counts to the right */
188 len = snprintf(NULL, 0, " (%lu/%lu)", f->totalnew, f->total);
189 if (len > maxcountlen)
190 maxcountlen = len;
191 }
192 }
193
194 /* write index file */
195 if (argc > 1) {
196 if (!(fpindex = fopen("index", "wb")))
197 err(1, "fopen: index");
198
199 for (i = 0; i < argc - 1; i++) {
200 f = &feeds[i];
201
202 /* append directory item to index */
203 fputs("1", fpindex);
204 fputs(f->totalnew ? "N " : " ", fpindex);
205
206 /* left align feed names and pad with spaces */
207 len = colw(f->name);
208 gophertext(fpindex, f->name);
209 for (; len < maxnamelen; len++)
210 fputs(" ", fpindex);
211
212 /* right align the item counts by padding with spaces */
213 snprintf(buf, sizeof(buf), " (%lu/%lu)", f->totalnew, f->total);
214 len = strlen(buf);
215 for (; len < maxcountlen; len++)
216 fputs(" ", fpindex);
217 fprintf(fpindex, "%s\t", buf);
218
219 gophertext(fpindex, prefixpath);
220 gophertext(fpindex, f->name);
221 fprintf(fpindex, "\t%s\t%s\r\n", host, port);
222 }
223 fputs(".\r\n", fpindex);
224 checkfileerror(fpindex, "index", 'w');
225 fclose(fpindex);
226 }
227
228 return 0;
229 }