sfeed_tail.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_tail.c (3882B)
       ---
            1 #include <sys/stat.h>
            2 #include <sys/types.h>
            3 
            4 #include <ctype.h>
            5 #include <err.h>
            6 #include <locale.h>
            7 #include <stdio.h>
            8 #include <stdlib.h>
            9 #include <string.h>
           10 #include <unistd.h>
           11 
           12 #include "tree.h"
           13 #include "util.h"
           14 
           15 static char *line;
           16 static size_t linesize;
           17 static int changed, firsttime = 1;
           18 static time_t comparetime;
           19 
           20 struct line {
           21         char *id;
           22         char *link;
           23         char *title;
           24         time_t timestamp;
           25         RB_ENTRY(line) entry;
           26 };
           27 
           28 int
           29 linecmp(struct line *e1, struct line *e2)
           30 {
           31         int r;
           32 
           33         if ((r = strcmp(e1->id, e2->id)))
           34                 return r;
           35         else if ((r = strcmp(e1->title, e2->title)))
           36                 return r;
           37         return strcmp(e1->link, e2->link);
           38 }
           39 RB_HEAD(linetree, line) head = RB_INITIALIZER(&head);
           40 RB_GENERATE_STATIC(linetree, line, entry, linecmp)
           41 
           42 /* remove old entries from the tree that won't be shown anyway. */
           43 static void
           44 gc(void)
           45 {
           46         struct line *l, *tmp;
           47 
           48         RB_FOREACH_SAFE(l, linetree, &head, tmp) {
           49                 if (l->timestamp < comparetime) {
           50                         free(l->id);
           51                         free(l->link);
           52                         free(l->title);
           53                         RB_REMOVE(linetree, &head, l);
           54                         free(l);
           55                 }
           56         }
           57 }
           58 
           59 static void
           60 printfeed(FILE *fp, const char *feedname)
           61 {
           62         struct line *add, search;
           63         char *fields[FieldLast];
           64         ssize_t linelen;
           65         time_t parsedtime;
           66         struct tm *tm;
           67 
           68         while ((linelen = getline(&line, &linesize, fp)) > 0) {
           69                 if (line[linelen - 1] == '\n')
           70                         line[--linelen] = '\0';
           71 
           72                 if (!parseline(line, fields))
           73                         break;
           74                 parsedtime = 0;
           75                 if (strtotime(fields[FieldUnixTimestamp], &parsedtime))
           76                         continue;
           77                 if (!(tm = localtime(&parsedtime)))
           78                         err(1, "localtime");
           79 
           80                 /* old news: skip */
           81                 if (parsedtime < comparetime)
           82                         continue;
           83 
           84                 search.id = fields[FieldId];
           85                 search.link = fields[FieldLink];
           86                 search.title = fields[FieldTitle];
           87                 search.timestamp = parsedtime;
           88                 if (RB_FIND(linetree, &head, &search))
           89                         continue;
           90 
           91                 changed = 1;
           92 
           93                 if (!(add = calloc(1, sizeof(*add))))
           94                         err(1, "calloc");
           95                 if (!(add->id = strdup(fields[FieldId])))
           96                         err(1, "strdup");
           97                 if (!(add->link = strdup(fields[FieldLink])))
           98                         err(1, "strdup");
           99                 if (!(add->title = strdup(fields[FieldTitle])))
          100                         err(1, "strdup");
          101                 add->timestamp = parsedtime;
          102                 RB_INSERT(linetree, &head, add);
          103 
          104                 if (feedname[0]) {
          105                         printutf8pad(stdout, feedname, 15, ' ');
          106                         fputs("  ", stdout);
          107                 }
          108 
          109                 fprintf(stdout, "%04d-%02d-%02d %02d:%02d  ",
          110                         tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
          111                         tm->tm_hour, tm->tm_min);
          112                 printutf8pad(stdout, fields[FieldTitle], 70, ' ');
          113                 printf(" %s\n", fields[FieldLink]);
          114         }
          115 }
          116 
          117 int
          118 main(int argc, char *argv[])
          119 {
          120         struct stat *stfiles, st;
          121         char *name;
          122         FILE *fp;
          123         int i, slept = 0;
          124 
          125         if (pledge("stdio rpath", NULL) == -1)
          126                 err(1, "pledge");
          127 
          128         if (argc <= 1) {
          129                 fprintf(stderr, "usage: %s <file>...\n", argv[0]);
          130                 return 1;
          131         }
          132 
          133         setlocale(LC_CTYPE, "");
          134 
          135         if (!(stfiles = calloc(argc - 1, sizeof(*stfiles))))
          136                 err(1, "calloc");
          137 
          138         while (1) {
          139                 changed = 0;
          140 
          141                 if ((comparetime = time(NULL)) == -1)
          142                         err(1, "time");
          143                 /* 1 day is old news */
          144                 comparetime -= 86400;
          145 
          146                 for (i = 1; i < argc; i++) {
          147                         if (!(fp = fopen(argv[i], "r"))) {
          148                                 if (firsttime)
          149                                         err(1, "fopen: %s", argv[i]);
          150                                 /* don't report when the file is missing after the first run */
          151                                 continue;
          152                         }
          153                         if (fstat(fileno(fp), &st) == -1) {
          154                                 if (firsttime)
          155                                         err(1, "fstat: %s", argv[i]);
          156                                 warn("fstat: %s", argv[i]);
          157                                 fclose(fp);
          158                                 continue;
          159                         }
          160 
          161                         /* did the file change? by size or modification time */
          162                         if (stfiles[i - 1].st_size != st.st_size ||
          163                             stfiles[i - 1].st_mtime != st.st_mtime) {
          164                                 name = ((name = strrchr(argv[i], '/'))) ? name + 1 : argv[i];
          165                                 printfeed(fp, name);
          166                                 if (ferror(fp))
          167                                         warn("ferror: %s", argv[i]);
          168                                 memcpy(&stfiles[i - 1], &st, sizeof(st));
          169                         }
          170 
          171                         fclose(fp);
          172                 }
          173 
          174                 /* "garbage collect" on a change or every 5 minutes */
          175                 if (changed || slept > 300) {
          176                         gc();
          177                         changed = 0;
          178                         slept = 0;
          179                 }
          180                 sleep(10);
          181                 slept += 10;
          182                 firsttime = 0;
          183         }
          184         return 0;
          185 }