]> git.tuebingen.mpg.de Git - tfortune.git/blob - tfortune.c
initial
[tfortune.git] / tfortune.c
1 /* SPDX-License-Identifier: GPL-2.0 */
2
3 #include <stdlib.h>
4 #include <inttypes.h>
5 #include <stdio.h>
6 #include <fcntl.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <unistd.h>
10 #include <assert.h>
11 #include <sys/mman.h>
12 #include <stdbool.h>
13 #include <string.h>
14 #include <time.h>
15 #include <limits.h>
16 #include <sys/time.h>
17 #include <lopsub.h>
18
19 #include "tf.h"
20 #include "tfortune.lsg.h"
21
22 #define TF_SEP "---- "
23
24 struct tf_map {
25         void *map;
26         off_t len;
27 };
28
29 struct tf_cookie {
30         unsigned input_num;
31         off_t offset, len;
32 };
33
34 static void mmap_file(const char *path, struct tf_map *map)
35 {
36         int fd, ret;
37
38         ret = open(path, 0);
39         if (ret < 0) {
40                 perror("open");
41                 exit(EXIT_FAILURE);
42         }
43         fd = ret;
44         map->len = lseek(fd, 0, SEEK_END);
45         if (map->len == (off_t)-1) {
46                 perror("lseek");
47                 exit(EXIT_FAILURE);
48         }
49         map->map = mmap(NULL, map->len, PROT_READ, MAP_PRIVATE, fd, 0);
50         if (map->map == MAP_FAILED) {
51                 perror("mmap");
52                 exit(EXIT_FAILURE);
53         }
54         ret = close(fd);
55         assert(ret >= 0);
56 }
57
58 static bool tag_given(const char *tag, const char *tags, size_t sz)
59 {
60         bool found = false;
61         char *p, *str = strndup(tags, sz);
62
63         assert(str);
64         p = strtok(str, ",");
65         while (p) {
66                 //fprintf(stderr, "sz: %zu, compare: %s <-> %.*s\n", sz, tag, (int) sz, p);
67                 if (strcmp(p, tag) == 0) {
68                         found = true;
69                         break;
70                 }
71                 p = strtok(NULL, ",");
72         }
73         free(str);
74         return found;
75 }
76
77 static bool tags_ok(const char *tags, size_t sz, struct lls_parse_result *lpr)
78 {
79         unsigned n, given;
80         const struct lls_opt_result *r;
81         const char *arg;
82
83         r = lls_opt_result(LSG_TFORTUNE_TFORTUNE_OPT_ACCEPT, lpr);
84         given = lls_opt_given(r);
85         if (given == 0)
86                 goto check_reject;
87         /* check if any of the given accept tags is set */
88         for (n = 0; n < given; n++) {
89                 arg = lls_string_val(n, r);
90                 if (tag_given(arg, tags, sz))
91                         goto check_reject;
92         }
93         return false; /* accept tag(s) given, but none was set */
94 check_reject:
95         /* check if none of the given reject tags is set */
96         r = lls_opt_result(LSG_TFORTUNE_TFORTUNE_OPT_REJECT, lpr);
97         given = lls_opt_given(r);
98         for (n = 0; n < given; n++) {
99                 arg = lls_string_val(n, r);
100                 if (tag_given(arg, tags, sz))
101                         return false;
102         }
103         return true;
104 }
105
106 static void print_tags(const char *tags, size_t sz, FILE *f)
107 {
108         char *p, *str = strndup(tags, sz);
109
110         assert(str);
111         p = strtok(str, ",");
112         while (p) {
113                 fprintf(f, "%s\n", p);
114                 p = strtok(NULL, ",");
115         }
116         free(str);
117 }
118
119 static void print_random_cookie(const struct tf_cookie *cookies,
120                 unsigned num_cookies, const struct tf_map *maps)
121 {
122         long unsigned r;
123         const struct tf_cookie *cookie;
124         struct timeval tv;
125
126         if (num_cookies == 0) {
127                 fprintf(stderr, "no matching cookie\n");
128                 return;
129         }
130         gettimeofday(&tv, NULL);
131         srandom((unsigned)tv.tv_usec);
132         r = ((num_cookies + 0.0) * (random() / (RAND_MAX + 1.0)));
133         cookie = cookies + r;
134         assert(r < num_cookies);
135         printf("%.*s", (int)cookie->len,
136                 (char *)maps[cookie->input_num].map + cookie->offset);
137 }
138
139 static void make_cookies(struct tf_map *maps, struct lls_parse_result *lpr)
140 {
141         struct tf_cookie *cookies = NULL;
142         unsigned n, cookies_size = 0, num_cookies = 0, num_inputs;
143         size_t sep_len = strlen(TF_SEP);
144         const struct lls_opt_result *r_s;
145         FILE *f = NULL;
146
147         r_s = lls_opt_result(LSG_TFORTUNE_TFORTUNE_OPT_STATISTICS, lpr);
148         if (lls_opt_given(r_s)) {
149                 f = popen("sort | uniq -c | sort -n", "w");
150                 assert(f);
151         }
152         num_inputs = lls_num_inputs(lpr);
153         for (n = 0; n < num_inputs; n++) {
154                 struct tf_map *m = maps + n;
155                 const char *start = m->map, *end = m->map + m->len;
156                 const char *buf = start, *cookie_start = start;
157
158                 while (buf < end) {
159                         struct tf_cookie *cookie;
160                         const char *p, *cr, *tags;
161                         size_t sz;
162
163                         cr = memchr(buf, '\n', end - buf);
164                         if (!cr)
165                                 break;
166                         p = cr + 1;
167                         if (!cookie_start)
168                                 cookie_start = p;
169                         if (p + sep_len >= end)
170                                 break;
171                         if (strncmp(p, TF_SEP, sep_len) != 0) {
172                                 buf = p;
173                                 continue;
174                         }
175                         tags = p + sep_len;
176                         cr = memchr(tags, '\n', end - tags);
177                         if (cr)
178                                 sz = cr - tags;
179                         else
180                                 sz = end - tags;
181                         if (!tags_ok(tags, sz, lpr)) {
182                                 if (!cr)
183                                         break;
184                                 buf = cr;
185                                 cookie_start = NULL;
186                                 continue;
187                         }
188                         num_cookies++;
189                         if (lls_opt_given(r_s)) {
190                                 print_tags(tags, sz, f);
191                                 if (!cr)
192                                         break;
193                                 buf = cr;
194                                 continue;
195                         }
196                         if (num_cookies > cookies_size) {
197                                 cookies_size = 2 * cookies_size + 1;
198                                 cookies = realloc(cookies,
199                                         cookies_size * sizeof(*cookies));
200                                 assert(cookies);
201                         }
202                         cookie = cookies + num_cookies - 1;
203                         cookie->input_num = n;
204                         cookie->offset = cookie_start - start;
205                         cookie->len = p - cookie_start;
206                         buf = p + sep_len;
207                         cookie_start = NULL;
208                 }
209         }
210         if (f)
211                 pclose(f);
212         if (!lls_opt_given(r_s))
213                 print_random_cookie(cookies, num_cookies, maps);
214         else
215                 printf("num cookies: %d\n", num_cookies);
216         free(cookies);
217 }
218
219 int main(int argc, char **argv)
220 {
221         char *errctx;
222         struct lls_parse_result *lpr;
223         int ret;
224         struct tf_map *maps;
225         unsigned n, num_inputs;
226         const struct lls_command *cmd = lls_cmd(0, tfortune_suite);
227
228         ret = lls_parse(argc, argv, cmd, &lpr, &errctx);
229         if (ret < 0) {
230                 fprintf(stderr, "%s: %s\n", errctx? errctx : "",
231                         lls_strerror(-ret));
232                 free(errctx);
233                 exit(EXIT_FAILURE);
234         }
235         if (lls_num_inputs(lpr) == 0) {
236                 char *help = lls_short_help(cmd);
237                 fprintf(stderr, "%s\n", help);
238                 free(help);
239                 ret = 0;
240                 goto free_lpr;
241         }
242         num_inputs = lls_num_inputs(lpr);
243         maps = xmalloc(num_inputs * sizeof(*maps));
244         for (n = 0; n < num_inputs; n++)
245                 mmap_file(lls_input(n, lpr), maps + n);
246         make_cookies(maps, lpr);
247         free(maps);
248         ret = EXIT_SUCCESS;
249 free_lpr:
250         lls_free_parse_result(lpr, cmd);
251         return ret;
252 }