#include #include #include #include #include #include "util.h" static struct feed f; static char *prefixpath = "/", *host = "127.0.0.1", *port = "70"; /* default */ static char *line; static size_t linesize; static time_t comparetime; /* Escape characters in gopher, CR and LF are ignored */ void gophertext(FILE *fp, const char *s) { for (; *s; s++) { switch (*s) { case '\r': /* ignore CR */ case '\n': /* ignore LF */ break; case '\t': fputs(" ", fp); break; default: putc(*s, fp); break; } } } static void printfeed(FILE *fpitems, FILE *fpin, struct feed *f) { struct uri u; char *fields[FieldLast]; char *itemhost, *itemport, *itempath, *itemquery, *itemfragment; ssize_t linelen; unsigned int isnew; struct tm rtm, *tm; time_t parsedtime; int itemtype; if (f->name[0]) { fprintf(fpitems, "i%s\t\t%s\t%s\r\n", f->name, host, port); fprintf(fpitems, "i\t\t%s\t%s\r\n", host, port); } while ((linelen = getline(&line, &linesize, fpin)) > 0 && !ferror(fpitems)) { if (line[linelen - 1] == '\n') line[--linelen] = '\0'; parseline(line, fields); itemhost = host; itemport = port; itemtype = 'i'; itempath = fields[FieldLink]; itemquery = ""; itemfragment = ""; if (fields[FieldLink][0]) { itemtype = 'h'; /* if it's a gopher URL then change it into a direntry */ if (!strncmp(fields[FieldLink], "gopher://", 9) && uri_parse(fields[FieldLink], &u) != -1) { itemhost = u.host; itemport = u.port[0] ? u.port : "70"; itemtype = '1'; itempath = u.path; itemquery = u.query; itemfragment = u.fragment; if (itempath[0] == '/') { itempath++; if (*itempath) { itemtype = *itempath; itempath++; } } } } parsedtime = 0; if (!strtotime(fields[FieldUnixTimestamp], &parsedtime) && (tm = localtime_r(&parsedtime, &rtm))) { isnew = (parsedtime >= comparetime) ? 1 : 0; f->totalnew += isnew; fprintf(fpitems, "%c%c %04d-%02d-%02d %02d:%02d ", itemtype, isnew ? 'N' : ' ', tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min); } else { fprintf(fpitems, "%c ", itemtype); } f->total++; gophertext(fpitems, fields[FieldTitle]); fputs("\t", fpitems); if (itemtype == 'h' && fields[FieldLink] == itempath) fputs("URL:", fpitems); gophertext(fpitems, itempath); if (itemquery[0]) { fputs("?", fpitems); gophertext(fpitems, itemquery); } if (itemfragment[0]) { fputs("#", fpitems); gophertext(fpitems, itemfragment); } fprintf(fpitems, "\t%s\t%s\r\n", itemhost, itemport); } fputs(".\r\n", fpitems); } int main(int argc, char *argv[]) { FILE *fpitems, *fpindex, *fp; char *name, *p, path[PATH_MAX + 1]; int i, r; if (argc == 1) { if (pledge("stdio", NULL) == -1) err(1, "pledge"); } else { if (unveil("/", "r") == -1) err(1, "unveil: /"); if (unveil(".", "rwc") == -1) err(1, "unveil: ."); if (pledge("stdio rpath wpath cpath", NULL) == -1) err(1, "pledge"); } if ((comparetime = time(NULL)) == (time_t)-1) errx(1, "time"); /* 1 day is old news */ comparetime -= 86400; if ((p = getenv("SFEED_GOPHER_HOST"))) host = p; if ((p = getenv("SFEED_GOPHER_PORT"))) port = p; if (argc == 1) { f.name = ""; printfeed(stdout, stdin, &f); checkfileerror(stdin, "", 'r'); checkfileerror(stdout, "", 'w'); } else { if ((p = getenv("SFEED_GOPHER_PATH"))) prefixpath = p; /* write main index page */ if (!(fpindex = fopen("index", "wb"))) err(1, "fopen: index"); for (i = 1; i < argc; i++) { memset(&f, 0, sizeof(f)); name = ((name = strrchr(argv[i], '/'))) ? name + 1 : argv[i]; f.name = name; if (!(fp = fopen(argv[i], "r"))) err(1, "fopen: %s", argv[i]); r = snprintf(path, sizeof(path), "%s", name); if (r < 0 || (size_t)r >= sizeof(path)) errx(1, "path truncation: %s", path); if (!(fpitems = fopen(path, "wb"))) err(1, "fopen"); printfeed(fpitems, fp, &f); checkfileerror(fp, argv[i], 'r'); checkfileerror(fpitems, path, 'w'); fclose(fp); fclose(fpitems); /* append directory item to index */ fputs("1", fpindex); gophertext(fpindex, name); fprintf(fpindex, " (%lu/%lu)\t", f.totalnew, f.total); gophertext(fpindex, prefixpath); gophertext(fpindex, path); fprintf(fpindex, "\t%s\t%s\r\n", host, port); } fputs(".\r\n", fpindex); checkfileerror(fpindex, "index", 'w'); fclose(fpindex); } return 0; }