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 }