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("&lt;",   fp); break;
           65                 case '>':  fputs("&gt;",   fp); break;
           66                 case '\'': fputs("&#39;",  fp); break;
           67                 case '&':  fputs("&amp;",  fp); break;
           68                 case '"':  fputs("&quot;", 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&nbsp;%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 }