diff options
| author | Andre Noll <maan@tuebingen.mpg.de> | 2018-01-11 23:40:23 +0100 |
|---|---|---|
| committer | Andre Noll <maan@tuebingen.mpg.de> | 2018-01-11 23:40:23 +0100 |
| commit | b95de7e4b629c3ffb90ae09b53d0178285e73629 (patch) | |
| tree | 2929492fa05274fc150e09c4821dbf52a65149f3 /tfortune.c | |
initialinitial
Diffstat (limited to 'tfortune.c')
| -rw-r--r-- | tfortune.c | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/tfortune.c b/tfortune.c new file mode 100644 index 0000000..7447059 --- /dev/null +++ b/tfortune.c @@ -0,0 +1,252 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include <stdlib.h> +#include <inttypes.h> +#include <stdio.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <assert.h> +#include <sys/mman.h> +#include <stdbool.h> +#include <string.h> +#include <time.h> +#include <limits.h> +#include <sys/time.h> +#include <lopsub.h> + +#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; +} |
