/* SPDX-License-Identifier: GPL-2.0 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tf.h" #include "tfortune.lsg.h" #define TF_SEP "---- " struct tf_map { void *map; off_t len; }; struct tf_cookie { unsigned input_num; off_t offset, len; }; static void mmap_file(const char *path, struct tf_map *map) { int fd, ret; ret = open(path, 0); if (ret < 0) { perror("open"); exit(EXIT_FAILURE); } fd = ret; map->len = lseek(fd, 0, SEEK_END); if (map->len == (off_t)-1) { perror("lseek"); exit(EXIT_FAILURE); } map->map = mmap(NULL, map->len, PROT_READ, MAP_PRIVATE, fd, 0); if (map->map == MAP_FAILED) { perror("mmap"); exit(EXIT_FAILURE); } ret = close(fd); assert(ret >= 0); } static bool tag_given(const char *tag, const char *tags, size_t sz) { bool found = false; char *p, *str = strndup(tags, sz); assert(str); p = strtok(str, ","); while (p) { //fprintf(stderr, "sz: %zu, compare: %s <-> %.*s\n", sz, tag, (int) sz, p); if (strcmp(p, tag) == 0) { found = true; break; } p = strtok(NULL, ","); } free(str); return found; } static bool tags_ok(const char *tags, size_t sz, struct lls_parse_result *lpr) { unsigned n, given; const struct lls_opt_result *r; const char *arg; r = lls_opt_result(LSG_TFORTUNE_TFORTUNE_OPT_ACCEPT, lpr); given = lls_opt_given(r); if (given == 0) goto check_reject; /* check if any of the given accept tags is set */ for (n = 0; n < given; n++) { arg = lls_string_val(n, r); if (tag_given(arg, tags, sz)) goto check_reject; } return false; /* accept tag(s) given, but none was set */ check_reject: /* check if none of the given reject tags is set */ r = lls_opt_result(LSG_TFORTUNE_TFORTUNE_OPT_REJECT, lpr); given = lls_opt_given(r); for (n = 0; n < given; n++) { arg = lls_string_val(n, r); if (tag_given(arg, tags, sz)) return false; } return true; } static void print_tags(const char *tags, size_t sz, FILE *f) { char *p, *str = strndup(tags, sz); assert(str); p = strtok(str, ","); while (p) { fprintf(f, "%s\n", p); p = strtok(NULL, ","); } free(str); } static void print_random_cookie(const struct tf_cookie *cookies, unsigned num_cookies, const struct tf_map *maps) { long unsigned r; const struct tf_cookie *cookie; struct timeval tv; if (num_cookies == 0) { fprintf(stderr, "no matching cookie\n"); return; } gettimeofday(&tv, NULL); srandom((unsigned)tv.tv_usec); r = ((num_cookies + 0.0) * (random() / (RAND_MAX + 1.0))); cookie = cookies + r; assert(r < num_cookies); printf("%.*s", (int)cookie->len, (char *)maps[cookie->input_num].map + cookie->offset); } static void make_cookies(struct tf_map *maps, struct lls_parse_result *lpr) { struct tf_cookie *cookies = NULL; unsigned n, cookies_size = 0, num_cookies = 0, num_inputs; size_t sep_len = strlen(TF_SEP); const struct lls_opt_result *r_s; FILE *f = NULL; r_s = lls_opt_result(LSG_TFORTUNE_TFORTUNE_OPT_STATISTICS, lpr); if (lls_opt_given(r_s)) { f = popen("sort | uniq -c | sort -n", "w"); assert(f); } num_inputs = lls_num_inputs(lpr); for (n = 0; n < num_inputs; n++) { struct tf_map *m = maps + n; const char *start = m->map, *end = m->map + m->len; const char *buf = start, *cookie_start = start; while (buf < end) { struct tf_cookie *cookie; const char *p, *cr, *tags; size_t sz; cr = memchr(buf, '\n', end - buf); if (!cr) break; p = cr + 1; if (!cookie_start) cookie_start = p; if (p + sep_len >= end) break; if (strncmp(p, TF_SEP, sep_len) != 0) { buf = p; continue; } tags = p + sep_len; cr = memchr(tags, '\n', end - tags); if (cr) sz = cr - tags; else sz = end - tags; if (!tags_ok(tags, sz, lpr)) { if (!cr) break; buf = cr; cookie_start = NULL; continue; } num_cookies++; if (lls_opt_given(r_s)) { print_tags(tags, sz, f); if (!cr) break; buf = cr; continue; } if (num_cookies > cookies_size) { cookies_size = 2 * cookies_size + 1; cookies = realloc(cookies, cookies_size * sizeof(*cookies)); assert(cookies); } cookie = cookies + num_cookies - 1; cookie->input_num = n; cookie->offset = cookie_start - start; cookie->len = p - cookie_start; buf = p + sep_len; cookie_start = NULL; } } if (f) pclose(f); if (!lls_opt_given(r_s)) print_random_cookie(cookies, num_cookies, maps); else printf("num cookies: %d\n", num_cookies); free(cookies); } int main(int argc, char **argv) { char *errctx; struct lls_parse_result *lpr; int ret; struct tf_map *maps; unsigned n, num_inputs; const struct lls_command *cmd = lls_cmd(0, tfortune_suite); ret = lls_parse(argc, argv, cmd, &lpr, &errctx); if (ret < 0) { fprintf(stderr, "%s: %s\n", errctx? errctx : "", lls_strerror(-ret)); free(errctx); exit(EXIT_FAILURE); } if (lls_num_inputs(lpr) == 0) { char *help = lls_short_help(cmd); fprintf(stderr, "%s\n", help); free(help); ret = 0; goto free_lpr; } num_inputs = lls_num_inputs(lpr); maps = xmalloc(num_inputs * sizeof(*maps)); for (n = 0; n < num_inputs; n++) mmap_file(lls_input(n, lpr), maps + n); make_cookies(maps, lpr); free(maps); ret = EXIT_SUCCESS; free_lpr: lls_free_parse_result(lpr, cmd); return ret; }