sfeed_frames_content.c - randomcrap - random crap programs of varying quality
(HTM) git clone git://git.codemadness.org/randomcrap
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
sfeed_frames_content.c (9838B)
---
1 #include <sys/stat.h>
2 #include <sys/types.h>
3
4 #include <err.h>
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <limits.h>
8 #include <stdarg.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <time.h>
13 #include <unistd.h>
14 #include <utime.h>
15
16 #include "util.h"
17
18 static struct feed **feeds;
19 static char *line;
20 static size_t linesize;
21 static struct timespec times[2];
22 static time_t comparetime;
23 static unsigned long totalnew, total;
24
25 /* Unescape / decode fields printed by string_print_encoded()
26 * "\\" to "\", "\t", to TAB, "\n" to newline. Unrecognised escape sequences
27 * are ignored: "\z" etc. */
28 static void
29 printcontent(const char *s, FILE *fp)
30 {
31 for (; *s; s++) {
32 switch (*s) {
33 case '\\':
34 switch (*(++s)) {
35 case '\0': return; /* ignore */
36 case '\\': putc('\\', fp); break;
37 case 't': putc('\t', fp); break;
38 case 'n': putc('\n', fp); break;
39 }
40 break;
41 default:
42 putc(*s, fp);
43 }
44 }
45 }
46
47 /* Unescape / decode fields printed by string_print_encoded()
48 * "\\" to "\", "\t", to TAB, "\n" to newline. Unrecognised escape sequences
49 * are ignored: "\z" etc. Encode HTML 2.0 / XML 1.0 entities. */
50 static void
51 printcontentxml(const char *s, FILE *fp)
52 {
53 for (; *s; s++) {
54 switch (*s) {
55 case '\\':
56 switch (*(++s)) {
57 case '\0': return; /* ignore */
58 case '\\': putc('\\', fp); break;
59 case 't': putc('\t', fp); break;
60 case 'n': putc('\n', fp); break;
61 }
62 break;
63 /* XML entities */
64 case '<': fputs("<", fp); break;
65 case '>': fputs(">", fp); break;
66 case '\'': fputs("'", fp); break;
67 case '&': fputs("&", fp); break;
68 case '"': fputs(""", fp); break;
69 default: putc(*s, fp);
70 }
71 }
72 }
73
74 /* normalize path names, transform to lower-case and replace non-alpha and
75 * non-digit with '-' */
76 static size_t
77 normalizepath(const char *path, char *buf, size_t bufsiz)
78 {
79 size_t i, r = 0;
80
81 for (i = 0; *path && i < bufsiz - 1; path++) {
82 if (ISALPHA((unsigned char)*path) || ISDIGIT((unsigned char)*path)) {
83 buf[i++] = TOLOWER((unsigned char)*path);
84 r = 0;
85 } else {
86 /* don't repeat '-', don't start with '-' */
87 if (!r && i)
88 buf[i++] = '-';
89 r++;
90 }
91 }
92 /* remove trailing '-' */
93 for (; i > 0 && (buf[i - 1] == '-'); i--)
94 ;
95
96 buf[i] = '\0';
97
98 return i;
99 }
100
101 static void
102 printfeed(FILE *fpitems, FILE *fpin, struct feed *f)
103 {
104 char dirpath[PATH_MAX], filepath[PATH_MAX];
105 char *fields[FieldLast], *feedname, name[128];
106 ssize_t linelen;
107 FILE *fpcontent = NULL;
108 unsigned int isnew;
109 struct tm rtm, *tm;
110 time_t parsedtime;
111 int fd, r;
112
113 if (f->name[0])
114 feedname = f->name;
115 else
116 feedname = "unnamed";
117
118 /* make directory for feedname */
119 if (!normalizepath(feedname, name, sizeof(name)))
120 return;
121
122 strlcpy(dirpath, name, sizeof(dirpath));
123
124 /* error creating directory and it doesn't exist. */
125 if (mkdir(dirpath, S_IRWXU | S_IRWXG | S_IRWXO) == -1 && errno != EEXIST)
126 err(1, "mkdir: %s", dirpath);
127
128 /* menu if not unnamed */
129 if (f->name[0]) {
130 fputs("<h2 id=\"", fpitems);
131 xmlencode(f->name, fpitems);
132 fputs("\"><a href=\"#", fpitems);
133 xmlencode(f->name, fpitems);
134 fputs("\">", fpitems);
135 xmlencode(f->name, fpitems);
136 fputs("</a></h2>\n", fpitems);
137 }
138
139 while ((linelen = getline(&line, &linesize, fpin)) > 0) {
140 if (line[linelen - 1] == '\n')
141 line[--linelen] = '\0';
142 parseline(line, fields);
143
144 parsedtime = 0;
145 if (strtotime(fields[FieldUnixTimestamp], &parsedtime))
146 continue;
147 if (!(tm = localtime_r(&parsedtime, &rtm)))
148 err(1, "localtime");
149
150 if (!normalizepath(fields[FieldTitle], name, sizeof(name)))
151 continue;
152
153 r = snprintf(filepath, sizeof(filepath), "%s/%s-%lld.html",
154 dirpath, name, (long long)parsedtime);
155 if (r == -1 || (size_t)r >= sizeof(filepath))
156 errx(1, "snprintf: path truncation: '%s/%s-%lld.html'",
157 dirpath, name, (long long)parsedtime);
158
159 /* content file doesn't exist yet and has error? */
160 if ((fd = open(filepath, O_CREAT | O_EXCL | O_WRONLY,
161 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) == -1) {
162 if (errno != EEXIST)
163 err(1, "open: %s", filepath);
164 } else {
165 if (!(fpcontent = fdopen(fd, "wb")))
166 err(1, "fdopen: %s", filepath);
167 fputs("<html><head>"
168 "<link rel=\"stylesheet\" type=\"text/css\" href=\"../../style.css\" />"
169 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />"
170 "</head>\n<body class=\"frame\">"
171 "<div class=\"content\"><h2>", fpcontent);
172
173 if (fields[FieldLink][0]) {
174 fputs("<a href=\"", fpcontent);
175 xmlencode(fields[FieldLink], fpcontent);
176 fputs("\">", fpcontent);
177 }
178 xmlencode(fields[FieldTitle], fpcontent);
179 if (fields[FieldLink][0])
180 fputs("</a>", fpcontent);
181 fputs("</h2>", fpcontent);
182
183 /* NOTE: this prints the raw HTML of the feed, this is
184 * potentially dangerous, it is left up to the
185 * user / browser to trust a feed its HTML content. */
186 if (!strcmp(fields[FieldContentType], "html")) {
187 printcontent(fields[FieldContent], fpcontent);
188 } else {
189 /* plain-text, wrap with <pre> */
190 fputs("<pre>", fpcontent);
191 printcontentxml(fields[FieldContent], fpcontent);
192 fputs("</pre>", fpcontent);
193 }
194 fputs("</div></body></html>\n", fpcontent);
195
196 /* set modified and access time of file to time of item. */
197 if (parsedtime) {
198 /* flush writes before setting atime and mtime
199 else the remaining (buffered) write can occur at
200 fclose() and overwrite our time again. */
201 fflush(fpcontent);
202
203 times[0].tv_sec = parsedtime;
204 times[1].tv_sec = parsedtime;
205
206 if (futimens(fd, times) == -1)
207 err(1, "futimens");
208 }
209 fclose(fpcontent);
210 }
211
212 isnew = (parsedtime >= comparetime) ? 1 : 0;
213 totalnew += isnew;
214 f->totalnew += isnew;
215 f->total++;
216 total++;
217
218 fprintf(fpitems, "%04d-%02d-%02d %02d:%02d ",
219 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
220 tm->tm_hour, tm->tm_min);
221
222 if (isnew)
223 fputs("<b><u>", fpitems);
224 fputs("<a href=\"", fpitems);
225 fputs(filepath, fpitems);
226 fputs("\" target=\"content\">", fpitems);
227 xmlencode(fields[FieldTitle], fpitems);
228 fputs("</a>", fpitems);
229 if (isnew)
230 fputs("</u></b>", fpitems);
231 fputs("\n", fpitems);
232 }
233 }
234
235 int
236 main(int argc, char *argv[])
237 {
238 FILE *fpindex, *fpitems, *fpmenu = NULL, *fp;
239 char *name;
240 int i, showsidebar = (argc > 1);
241 struct feed *f;
242
243 if (unveil("/", "r") == -1)
244 err(1, "unveil: /");
245 if (unveil(".", "rwc") == -1)
246 err(1, "unveil: .");
247 if (pledge("stdio rpath wpath cpath fattr", NULL) == -1)
248 err(1, "pledge");
249
250 if (!(feeds = calloc(argc, sizeof(struct feed *))))
251 err(1, "calloc");
252
253 if ((comparetime = getcomparetime()) == -1)
254 errx(1, "getcomparetime");
255
256 /* write main index page */
257 if (!(fpindex = fopen("index.html", "wb")))
258 err(1, "fopen: index.html");
259 if (!(fpitems = fopen("items.html", "wb")))
260 err(1, "fopen: items.html");
261 if (showsidebar && !(fpmenu = fopen("menu.html", "wb")))
262 err(1, "fopen: menu.html");
263 fputs("<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"../style.css\" />"
264 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" /></head>"
265 "<body class=\"frame\"><div id=\"items\"><pre>", fpitems);
266
267 if (argc == 1) {
268 if (!(feeds[0] = calloc(1, sizeof(struct feed))))
269 err(1, "calloc");
270 feeds[0]->name = "";
271 printfeed(fpitems, stdin, feeds[0]);
272 checkfileerror(stdin, "<stdin>", 'r');
273 } else {
274 for (i = 1; i < argc; i++) {
275 if (!(feeds[i - 1] = calloc(1, sizeof(struct feed))))
276 err(1, "calloc");
277 name = ((name = strrchr(argv[i], '/'))) ? name + 1 : argv[i];
278 feeds[i - 1]->name = name;
279
280 if (!(fp = fopen(argv[i], "r")))
281 err(1, "fopen: %s", argv[i]);
282 printfeed(fpitems, fp, feeds[i - 1]);
283 checkfileerror(fp, argv[i], 'r');
284 checkfileerror(fpitems, "items.html", 'w');
285 fclose(fp);
286 }
287 }
288 fputs("</pre>\n</div></body>\n</html>\n", fpitems); /* div items */
289
290 if (showsidebar) {
291 fputs("<html><head>"
292 "<link rel=\"stylesheet\" type=\"text/css\" href=\"../style.css\" />\n"
293 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
294 "</head><body class=\"frame\"><div id=\"sidebar\">", fpmenu);
295
296 for (i = 1; i < argc; i++) {
297 f = feeds[i - 1];
298 if (f->totalnew)
299 fputs("<a class=\"n\" href=\"items.html#", fpmenu);
300 else
301 fputs("<a href=\"items.html#", fpmenu);
302 xmlencode(f->name, fpmenu);
303 fputs("\" target=\"items\">", fpmenu);
304 if (f->totalnew > 0)
305 fputs("<b><u>", fpmenu);
306 xmlencode(f->name, fpmenu);
307 fprintf(fpmenu, " (%lu)", f->totalnew);
308 if (f->totalnew > 0)
309 fputs("</u></b>", fpmenu);
310 fputs("</a><br/>\n", fpmenu);
311 }
312 fputs("</div></body></html>\n", fpmenu);
313 }
314 fputs("<!DOCTYPE html><html><head>\n\t<title>(", fpindex);
315 fprintf(fpindex, "%lu/%lu", totalnew, total);
316 fputs(") - Newsfeed</title>\n\t<link rel=\"stylesheet\" type=\"text/css\" href=\"../style.css\" />\n"
317 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
318 "</head>\n", fpindex);
319 if (showsidebar) {
320 fputs("<frameset framespacing=\"0\" cols=\"200,*\" frameborder=\"1\">\n"
321 " <frame name=\"menu\" src=\"menu.html\" target=\"menu\">\n", fpindex);
322 } else {
323 fputs("<frameset framespacing=\"0\" cols=\"*\" frameborder=\"1\">\n", fpindex);
324 }
325 fputs("\t<frameset id=\"frameset\" framespacing=\"0\" cols=\"50%,50%\" frameborder=\"1\">\n"
326 "\t\t<frame name=\"items\" src=\"items.html\" target=\"items\">\n"
327 "\t\t<frame name=\"content\" target=\"content\">\n"
328 "\t</frameset>\n"
329 "</frameset>\n"
330 "</html>\n", fpindex);
331
332 checkfileerror(fpindex, "index.html", 'w');
333 checkfileerror(fpitems, "items.html", 'w');
334
335 fclose(fpitems);
336 fclose(fpindex);
337 if (fpmenu) {
338 checkfileerror(fpmenu, "menu.html", 'w');
339 fclose(fpmenu);
340 }
341
342 return 0;
343 }