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 }