Document changes, upload to unstable.
[tfortune.git] / tfortune.c
1 /* SPDX-License-Identifier: GPL-3.0-only */
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 static struct lls_parse_result *lpr, *sublpr;
25 #define CMD_PTR(_cname) lls_cmd(LSG_TFORTUNE_CMD_ ## _cname, tfortune_suite)
26 #define OPT_RESULT(_cname, _oname) (lls_opt_result(\
27         LSG_TFORTUNE_ ## _cname ## _OPT_ ## _oname, \
28         (CMD_PTR(_cname) == CMD_PTR(TFORTUNE))? lpr : sublpr))
29 #define OPT_GIVEN(_cname, _oname) (lls_opt_given(OPT_RESULT(_cname, _oname)))
30 #define OPT_STRING_VAL(_cname, _oname) (lls_string_val(0, \
31         OPT_RESULT(_cname, _oname)))
32 #define OPT_UINT32_VAL(_cname, _oname) (lls_uint32_val(0, \
33                 OPT_RESULT(_cname, _oname)))
34
35 int loglevel_arg_val;
36
37 struct tf_user_data {int (*handler)(void);};
38 #define EXPORT_CMD_HANDLER(_cmd) const struct tf_user_data \
39         lsg_tfortune_com_ ## _cmd ## _user_data = { \
40                 .handler = com_ ## _cmd \
41         };
42
43 struct map_chunk {
44         const char *base;
45         size_t len;
46 };
47
48 struct epigram {
49         struct map_chunk epi, tags;
50 };
51
52 static int lopsub_error(int lopsub_ret, char **errctx)
53 {
54         const char *msg = lls_strerror(-lopsub_ret);
55         if (*errctx)
56                 ERROR_LOG("%s: %s\n", *errctx, msg);
57         else
58                 ERROR_LOG("%s\n", msg);
59         free(*errctx);
60         *errctx = NULL;
61         return -E_LOPSUB;
62 }
63
64 /* per epigram context for the tag expression parser */
65 struct epi_properties {
66         const struct map_chunk *chunk; /* only the epigram */
67         struct linhash_table *tagtab;
68         unsigned num_tags;
69 };
70
71 unsigned epi_len(const struct epi_properties *props)
72 {
73         return props->chunk->len;
74 }
75
76 char *epi_text(const struct epi_properties *props)
77 {
78         const char *txt = props->chunk->base;
79         char *result = malloc(props->chunk->len + 1);
80         memcpy(result, txt, props->chunk->len);
81         result[props->chunk->len] = '\0';
82         return result;
83 }
84
85 bool epi_has_tag(const char *tag, const struct epi_properties *props)
86 {
87         return linhash_lookup(tag, props->tagtab);
88 }
89
90 struct tag_iter {
91         char *str;
92         char *saveptr; /* for strtok_r(3) */
93         char *token;
94 };
95
96 static struct tag_iter *tag_iter_new(const struct map_chunk *tags)
97 {
98         struct tag_iter *titer = xmalloc(sizeof(*titer));
99
100         titer->str = xmalloc(tags->len + 1);
101         memcpy(titer->str, tags->base, tags->len);
102         titer->str[tags->len] = '\0';
103         titer->token = strtok_r(titer->str, ",", &titer->saveptr);
104         return titer;
105 }
106
107 static const char *tag_iter_get(const struct tag_iter *titer)
108 {
109         return titer->token;
110 }
111
112 static void tag_iter_next(struct tag_iter *titer)
113 {
114         titer->token = strtok_r(NULL, ",", &titer->saveptr);
115 }
116
117 static void tag_iter_free(struct tag_iter *titer)
118 {
119         if (!titer)
120                 return;
121         free(titer->str);
122         free(titer);
123 }
124
125 static bool epi_admissible(const struct epigram *epi,
126                 const struct txp_context *ast)
127 {
128         bool admissible;
129         const char *p;
130         struct epi_properties props = {
131                 .chunk = &epi->epi,
132                 .tagtab = linhash_new(3),
133                 .num_tags = 0,
134         };
135         struct tag_iter *titer;
136
137
138         for (
139                 titer = tag_iter_new(&epi->tags);
140                 (p = tag_iter_get(titer));
141                 tag_iter_next(titer)
142         ) {
143                 struct linhash_item item = {.key = p};
144                 linhash_insert(&item, props.tagtab, NULL);
145                 props.num_tags++;
146         }
147
148         admissible = txp_eval_ast(ast, &props);
149         linhash_free(props.tagtab);
150         tag_iter_free(titer);
151         return admissible;
152 }
153
154 static void print_epigram(const struct epigram *epi, bool print_tags)
155 {
156         printf("%.*s", (int)epi->epi.len, epi->epi.base);
157         if (print_tags)
158                 printf(TF_SEP "%.*s\n", (int)epi->tags.len, epi->tags.base);
159 }
160
161 static void print_admissible_epigrams(const struct epigram *epis,
162                 unsigned num_epis)
163 {
164         unsigned n;
165
166         for (n = 0; n < num_epis; n++)
167                 print_epigram(epis + n, OPT_GIVEN(PRINT, TAGS));
168 }
169
170 static void print_random_epigram(const struct epigram *epis, unsigned num_epis)
171 {
172         long unsigned r;
173         const struct epigram *epi;
174         struct timeval tv;
175
176         if (num_epis == 0) {
177                 ERROR_LOG("no matching epigram\n");
178                 return;
179         }
180         gettimeofday(&tv, NULL);
181         srandom((unsigned)tv.tv_usec);
182         r = (num_epis + 0.0) * (random() / (RAND_MAX + 1.0));
183         assert(r < num_epis);
184         epi = epis + r;
185         print_epigram(epi, OPT_GIVEN(PRINT, TAGS));
186 }
187
188 static int read_tag_expression(struct iovec *result)
189 {
190         const char *tx = OPT_STRING_VAL(PRINT, EXPRESSION);
191         int ret, fd;
192
193         assert(tx);
194         if (strcmp(tx, "-")) {
195                 char *filename;
196                 if (tx[0] != '/') {
197                         char *home = get_homedir();
198                         xasprintf(&filename, "%s/.tfortune/expressions/%s",
199                                 home, tx);
200                         free(home);
201                 } else
202                         filename = xstrdup(tx);
203                 ret = open(filename, O_RDONLY);
204                 if (ret < 0) {
205                         ret = -ERRNO_TO_TF_ERROR(errno);
206                         ERROR_LOG("could not open %s\n", filename);
207                 }
208                 free(filename);
209                 if (ret < 0)
210                         return ret;
211                 fd = ret;
212         } else
213                 fd = STDIN_FILENO;
214         ret = fd2buf(fd, result);
215         close(fd);
216         return ret;
217 }
218
219 static int tx2ast(const struct iovec *tx, struct txp_context **ast)
220 {
221         int ret;
222         char *errmsg;
223
224         ret = txp_init(tx, ast, &errmsg);
225         if (ret < 0) {
226                 ERROR_LOG("could not parse tag expression: %s\n", errmsg);
227                 free(errmsg);
228                 return ret;
229         }
230         return 1;
231 }
232
233 struct epi_iter {
234         struct iovec *maps;
235         unsigned num_maps;
236         unsigned map_num;
237         struct epigram epi;
238         unsigned num_epis;
239 };
240
241 static bool get_next_epi(struct epi_iter *eiter)
242 {
243         const char *epi_start = NULL;
244
245         for (; eiter->map_num < eiter->num_maps; eiter->map_num++) {
246                 struct iovec *iov = eiter->maps + eiter->map_num;
247                 const char *buf, *end = iov->iov_base + iov->iov_len;
248
249                 if (!epi_start && eiter->epi.tags.base)
250                         epi_start = eiter->epi.tags.base
251                                 + eiter->epi.tags.len + 1;
252                 else
253                         epi_start = iov->iov_base;
254                 buf = epi_start;
255                 while (buf < end) {
256                         const size_t sep_len = strlen(TF_SEP);
257                         const char *p, *cr, *tags;
258                         size_t tag_len;
259
260                         cr = memchr(buf, '\n', end - buf);
261                         if (!cr)
262                                 break;
263                         p = cr + 1;
264                         if (p + sep_len >= end)
265                                 break;
266                         if (strncmp(p, TF_SEP, sep_len) != 0) {
267                                 buf = p;
268                                 continue;
269                         }
270                         tags = p + sep_len;
271                         cr = memchr(tags, '\n', end - tags);
272                         if (cr)
273                                 tag_len = cr - tags;
274                         else
275                                 tag_len = end - tags;
276                         eiter->epi.epi.base = epi_start;
277                         eiter->epi.epi.len = p - epi_start;
278                         eiter->epi.tags.base = tags;
279                         eiter->epi.tags.len = tag_len;
280                         eiter->num_epis++;
281                         return true;
282                 }
283         }
284         eiter->epi.epi.base = NULL;
285         eiter->epi.epi.len = 0;
286         eiter->epi.tags.base = NULL;
287         eiter->epi.tags.len = 0;
288         return false;
289 }
290
291 static char *get_basedir(void)
292 {
293         char *home, *basedir;
294         if (OPT_GIVEN(TFORTUNE, BASEDIR))
295                 return xstrdup(OPT_STRING_VAL(TFORTUNE, BASEDIR));
296         home = get_homedir();
297         xasprintf(&basedir, "%s/.tfortune", home);
298         free(home);
299         return basedir;
300 }
301
302 static char *get_epidir(void)
303 {
304         char *basedir, *epidir;
305         struct stat s;
306
307         basedir = get_basedir();
308         xasprintf(&epidir, "%s/epigrams", basedir);
309         free(basedir);
310         if (!OPT_GIVEN(TFORTUNE, BASEDIR) && stat(epidir, &s) < 0) {
311                 free(epidir);
312                 epidir = xstrdup(DATADIR "/tfortunes/epigrams");
313                 INFO_LOG("falling back to system-wide epidir %s\n", epidir);
314         }
315         return epidir;
316 }
317
318 static char *get_xdir(void)
319 {
320         char *basedir = get_basedir(), *xdir;
321         xasprintf(&xdir, "%s/expressions", basedir);
322         free(basedir);
323         return xdir;
324 }
325
326 static struct epi_iter *epi_iter_new(void)
327 {
328         struct epi_iter *eiter = xmalloc(sizeof(*eiter));
329         unsigned num_inputs = lls_num_inputs(sublpr);
330
331         if (num_inputs == 0) {
332                 struct regfile_iter *riter;
333                 struct iovec iov;
334                 char *epidir = get_epidir();
335
336                 regfile_iter_new(epidir, &riter);
337                 free(epidir);
338                 eiter->maps = NULL;
339                 eiter->num_maps = 0;
340                 for (;
341                         regfile_iter_map(riter, &iov);
342                         regfile_iter_next(riter)
343                 ) {
344                         eiter->num_maps++;
345                         eiter->maps = xrealloc(eiter->maps,
346                                 eiter->num_maps * sizeof(*eiter->maps));
347                         eiter->maps[eiter->num_maps - 1] = iov;
348                 }
349                 regfile_iter_free(riter);
350         } else {
351                 unsigned n;
352                 eiter->maps = xmalloc(num_inputs * sizeof(*eiter->maps));
353                 for (n = 0; n < num_inputs; n++)
354                         mmap_file(lls_input(n, sublpr), eiter->maps + n);
355                 eiter->num_maps = num_inputs;
356         }
357         eiter->map_num = 0;
358         eiter->epi.epi.base = NULL;
359         eiter->epi.epi.len = 0;
360         eiter->epi.tags.base = NULL;
361         eiter->epi.tags.len = 0;
362         eiter->num_epis = 0;
363         get_next_epi(eiter);
364         return eiter;
365 }
366
367 static const struct epigram *epi_iter_get(const struct epi_iter *eiter)
368 {
369         return (eiter->epi.epi.base && eiter->epi.tags.base)?
370                 &eiter->epi : NULL;
371 }
372
373 static unsigned epi_iter_num_maps(const struct epi_iter *eiter)
374 {
375         return eiter->num_maps;
376 }
377
378 static unsigned epi_iter_num_epis(const struct epi_iter *eiter)
379 {
380         return eiter->num_epis;
381 }
382
383 static void epi_iter_next(struct epi_iter *eiter)
384 {
385         get_next_epi(eiter);
386 }
387
388 static void epi_iter_free(struct epi_iter *eiter)
389 {
390         unsigned n;
391
392         if (!eiter)
393                 return;
394         for (n = 0; n < eiter->num_maps; n++)
395                 munmap(eiter->maps[n].iov_base, eiter->maps[n].iov_len);
396         free(eiter->maps);
397         free(eiter);
398 }
399
400 static int com_print(void)
401 {
402         int ret;
403         struct epigram *epis = NULL;
404         unsigned epis_sz = 0, nae = 0; /* number of admissible epis */
405         struct iovec tx;
406         struct txp_context *ast;
407         struct epi_iter *eiter;
408         const struct epigram *epi;
409
410         ret = read_tag_expression(&tx);
411         if (ret < 0)
412                 return ret;
413         ret = tx2ast(&tx, &ast);
414         if (ret < 0)
415                 goto free_tx;
416         for (
417                 eiter = epi_iter_new();
418                 (epi = epi_iter_get(eiter));
419                 epi_iter_next(eiter)
420         ) {
421                 if (!epi_admissible(epi, ast))
422                         continue;
423                 if (nae >= epis_sz) {
424                         epis_sz = 2 * epis_sz + 1;
425                         epis = xrealloc(epis, epis_sz * sizeof(*epis));
426                 }
427                 epis[nae++] = *epi;
428         }
429         if (OPT_GIVEN(PRINT, ALL))
430                 print_admissible_epigrams(epis, nae);
431         else
432                 print_random_epigram(epis, nae);
433         epi_iter_free(eiter);
434         free(epis);
435         txp_free(ast);
436         ret = 1;
437 free_tx:
438         free(tx.iov_base);
439         return ret;
440 }
441 EXPORT_CMD_HANDLER(print);
442
443 static char *get_editor(void)
444 {
445         char *val = getenv("TFORTUNE_EDITOR");
446
447         if (val && val[0])
448                 return xstrdup(val);
449         val = getenv("EDITOR");
450         if (val && val[0])
451                 return xstrdup(val);
452         return xstrdup("vi");
453 }
454
455 static void open_editor(const char *dir)
456 {
457         char *editor;
458         char **argv;
459         pid_t pid;
460         unsigned n, num_inputs = lls_num_inputs(sublpr);
461
462         if ((pid = fork()) < 0) {
463                 EMERG_LOG("fork error: %s\n", strerror(errno));
464                 exit(EXIT_FAILURE);
465         }
466         if (pid) { /* parent */
467                 wait(NULL);
468                 return;
469         }
470         editor = get_editor();
471         argv = xmalloc((num_inputs + 2) * sizeof(*argv));
472         argv[0] = editor;
473         for (n = 0; n < num_inputs; n++)
474                 xasprintf(&argv[n + 1], "%s/%s", dir, lls_input(n, sublpr));
475         argv[num_inputs + 1] = NULL;
476         execvp(editor, argv);
477         EMERG_LOG("execvp error: %s\n", strerror(errno));
478         _exit(EXIT_FAILURE);
479 }
480
481 static int create_dir(const char *path)
482 {
483         int ret;
484
485         ret = mkdir(path, 0777); /* rely on umask */
486         if (ret < 0) {
487                 if (errno == EEXIST)
488                         return 0;
489                 ERROR_LOG("could not create %s\n", path);
490                 return -ERRNO_TO_TF_ERROR(errno);
491         }
492         NOTICE_LOG("created directory %s\n", path);
493         return 1;
494 }
495
496 static int create_basedir(void)
497 {
498         char *basedir;
499         int ret;
500
501         basedir = get_basedir();
502         ret = create_dir(basedir);
503         free(basedir);
504         return ret;
505 }
506
507 static int generic_edit(const char *dir)
508 {
509         char *errctx;
510         int ret;
511         bool basedir_given = OPT_GIVEN(TFORTUNE, BASEDIR);
512
513         ret = lls_check_arg_count(sublpr, 1, INT_MAX, &errctx);
514         if (ret < 0) {
515                 ret = lopsub_error(ret, &errctx);
516                 return ret;
517         }
518         if (!basedir_given) {
519                 ret = create_basedir();
520                 if (ret < 0)
521                         return ret;
522                 ret = create_dir(dir);
523                 if (ret < 0)
524                         return ret;
525         }
526         open_editor(dir);
527         ret = 1;
528         return ret;
529 }
530
531 static int com_ede(void)
532 {
533         int ret;
534         char *epidir = get_epidir();
535         ret = generic_edit(epidir);
536         free(epidir);
537         return ret;
538 }
539 EXPORT_CMD_HANDLER(ede);
540
541 static int com_edx(void)
542 {
543         int ret;
544         char *xdir = get_xdir();
545         ret = generic_edit(xdir);
546         free(xdir);
547         return ret;
548 }
549 EXPORT_CMD_HANDLER(edx);
550
551 static int item_alpha_compare(const struct linhash_item **i1,
552                 const struct linhash_item **i2)
553 {
554         return strcmp((*i1)->key, (*i2)->key);
555 }
556
557 static int item_num_compare(const struct linhash_item **i1,
558                 const struct linhash_item **i2)
559 {
560         long unsigned v1 = (long unsigned)(*i1)->object;
561         long unsigned v2 = (long unsigned)(*i2)->object;
562
563         return v1 < v2? -1 : (v1 == v2? 0 : 1);
564 }
565
566 struct dentry {
567         char mode[11];
568         nlink_t nlink;
569         char *user, *group;
570         off_t size;
571         uint64_t mtime;
572         char *name;
573 };
574
575 static void make_dentry(const char *name, const struct stat *stat,
576                 struct dentry *d)
577 {
578         mode_t m = stat->st_mode;
579         struct group *g;
580         struct passwd *pwentry;
581
582         sprintf(d->mode, "----------");
583         if (S_ISREG(m))
584                 d->mode[0] = '-';
585         else if (S_ISDIR(m))
586                 d->mode[0] = 'd';
587         else if (S_ISCHR(m))
588                 d->mode[0] = 'c';
589         else if ((S_ISBLK(m)))
590                 d->mode[0] = 'b';
591         else if (S_ISLNK(m))
592                 d->mode[0] = 'l';
593         else if (S_ISFIFO(m))
594                 d->mode[0] = 'p';
595         else if ((S_ISSOCK(m)))
596                 d->mode[0] = 's';
597         else
598                 d->mode[0] = '?';
599
600         if (m & S_IRUSR)
601                 d->mode[1] = 'r';
602         if (m & S_IWUSR)
603                 d->mode[2] = 'w';
604         if (m & S_IXUSR) {
605                 if (m & S_ISUID)
606                         d->mode[3] = 's';
607                 else
608                         d->mode[3] = 'x';
609         } else if (m & S_ISUID)
610                 d->mode[3] = 'S';
611
612         if (m & S_IRGRP)
613                 d->mode[4] = 'r';
614         if (m & S_IWGRP)
615                 d->mode[5] = 'w';
616         if (m & S_IXGRP) {
617                 if (m & S_ISGID)
618                         d->mode[6] = 's';
619                 else
620                         d->mode[6] = 'x';
621         } else if (m & S_ISGID)
622                 d->mode[6] = 'S';
623
624         if (m & S_IROTH)
625                 d->mode[7] = 'r';
626         if (m & S_IWOTH)
627                 d->mode[8] = 'w';
628         if (m & S_IXOTH) {
629                 if (m & S_ISVTX)
630                         d->mode[9] = 't';
631                 else
632                         d->mode[9] = 'x';
633         } else if (m & S_ISVTX)
634                 d->mode[9] = 'T';
635
636         d->nlink = stat->st_nlink;
637
638         pwentry = getpwuid(stat->st_uid);
639         if (pwentry && pwentry->pw_name)
640                 d->user = xstrdup(pwentry->pw_name);
641         else
642                 xasprintf(&d->user, "%u", stat->st_uid);
643
644         g = getgrgid(stat->st_gid);
645         if (g && g->gr_name)
646                 d->group = xstrdup(g->gr_name);
647         else
648                 xasprintf(&d->group, "%u", stat->st_gid);
649         d->size = stat->st_size;
650         d->mtime = stat->st_mtime;
651         d->name = xstrdup(name);
652 }
653
654 static int num_digits(uint64_t x)
655 {
656         unsigned n = 1;
657
658         if (x != 0)
659                 while (x > 9) {
660                         x /= 10;
661                         n++;
662                 }
663         return n;
664 }
665
666 enum var_length_dentry_fields {
667         VLDF_NLINKS,
668         VLDF_USER,
669         VLDF_GROUP,
670         VLDF_SIZE,
671         NUM_VLDF
672 };
673
674 static void update_field_field_widths(int field_widths[NUM_VLDF],
675                 const struct dentry *d)
676 {
677         int *w, n;
678
679         w = field_widths + VLDF_NLINKS;
680         n = num_digits(d->nlink);
681         *w = MAX(*w, n);
682
683         w = field_widths + VLDF_USER;
684         n = strlen(d->user);
685         *w = MAX(*w, n);
686
687         w = field_widths + VLDF_GROUP;
688         n = strlen(d->group);
689         *w = MAX(*w, n);
690
691         w = field_widths + VLDF_SIZE;
692         n = num_digits(d->size);
693         *w = MAX(*w, n);
694 }
695
696 static void format_time(uint64_t seconds, uint64_t now, struct iovec *result)
697 {
698         struct tm *tm;
699         const uint64_t m = 6 * 30 * 24 * 3600; /* six months */
700         size_t nbytes;
701
702         tm = localtime((time_t *)&seconds);
703         assert(tm);
704
705         if (seconds > now - m && seconds < now + m) {
706                 nbytes = strftime(result->iov_base, result->iov_len,
707                         "%b %e %k:%M", tm);
708                 assert(nbytes > 0);
709         } else {
710                 nbytes = strftime(result->iov_base, result->iov_len,
711                         "%b %e  %Y", tm);
712                 assert(nbytes > 0);
713         }
714 }
715
716 static int list_directory(const char *dir, bool long_listing)
717 {
718         struct regfile_iter *riter;
719         const char *basename;
720         int ret, field_widths[NUM_VLDF] = {0};
721         struct dentry *dentries = NULL;
722         unsigned n, num_dentries = 0, dentries_size = 0;
723         struct timespec now;
724
725         for (
726                 regfile_iter_new(dir, &riter);
727                 (basename = regfile_iter_basename(riter));
728                 regfile_iter_next(riter)
729         ) {
730                 const struct stat *stat;
731                 struct dentry *dentry;
732                 if (!long_listing) {
733                         printf("%s\n", basename);
734                         continue;
735                 }
736                 num_dentries++;
737                 if (num_dentries > dentries_size) {
738                         dentries_size = 2 * dentries_size + 1;
739                         dentries = xrealloc(dentries,
740                                 dentries_size * sizeof(*dentries));
741                 }
742                 dentry = dentries + num_dentries - 1;
743                 stat = regfile_iter_stat(riter);
744                 make_dentry(basename, stat, dentry);
745                 update_field_field_widths(field_widths, dentry);
746         }
747         regfile_iter_free(riter);
748         if (!long_listing)
749                 return 0;
750         ret = clock_gettime(CLOCK_REALTIME, &now);
751         assert(ret == 0);
752         for (n = 0; n < num_dentries; n++) {
753                 struct dentry *d = dentries + n;
754                 char buf[30];
755                 struct iovec iov = {.iov_base = buf, .iov_len = sizeof(buf)};
756
757                 format_time(d->mtime, now.tv_sec, &iov);
758                 printf("%s %*lu %*s %*s %*" PRIu64 " %s %s\n",
759                         d->mode,
760                         field_widths[VLDF_NLINKS], (long unsigned)d->nlink,
761                         field_widths[VLDF_USER], d->user,
762                         field_widths[VLDF_GROUP], d->group,
763                         field_widths[VLDF_SIZE], (uint64_t)d->size,
764                         buf,
765                         d->name
766                 );
767                 free(d->user);
768                 free(d->group);
769                 free(d->name);
770         }
771         free(dentries);
772         return 1;
773 }
774
775 static int com_lse(void)
776 {
777         int ret;
778         char *dir = get_epidir();
779
780         ret = list_directory(dir, OPT_GIVEN(LSE, LONG));
781         free(dir);
782         return ret;
783 }
784 EXPORT_CMD_HANDLER(lse);
785
786 static int com_lsx(void)
787 {
788         int ret;
789         char *dir = get_xdir();
790
791         ret = list_directory(dir, OPT_GIVEN(LSE, LONG));
792         free(dir);
793         return ret;
794 }
795 EXPORT_CMD_HANDLER(lsx);
796
797 static struct linhash_table *hash_tags(unsigned *num_epi_files,
798                 unsigned *num_epis)
799 {
800         struct linhash_table *tagtab = linhash_new(3);
801         struct epi_iter *eiter;
802         const struct epigram *epi;
803
804         for (
805                 eiter = epi_iter_new();
806                 (epi = epi_iter_get(eiter));
807                 epi_iter_next(eiter)
808         ) {
809                 struct tag_iter *titer;
810                 const char *tag;
811                 for (
812                         titer = tag_iter_new(&epi->tags);
813                         (tag = tag_iter_get(titer));
814                         tag_iter_next(titer)
815                 ) {
816                         struct linhash_item item = {
817                                 .key = xstrdup(tag),
818                                 .object = (void *)1LU
819                         };
820                         void **object;
821                         if (linhash_insert(&item, tagtab, &object) < 0) {
822                                 long unsigned val = (long unsigned)*object;
823                                 val++;
824                                 *object = (void *)val;
825                                 free((char *)item.key);
826                         }
827                 }
828                 tag_iter_free(titer);
829         }
830         if (num_epi_files)
831                 *num_epi_files = epi_iter_num_maps(eiter);
832         if (num_epis)
833                 *num_epis = epi_iter_num_epis(eiter);
834         epi_iter_free(eiter);
835         return tagtab;
836 }
837
838 static int com_lst(void)
839 {
840         struct linhash_table *tagtab;
841         struct linhash_iterator *liter;
842         struct linhash_item *itemp;
843         linhash_comparator *comp = OPT_GIVEN(LST, SORT_BY_COUNT)?
844                 item_num_compare : item_alpha_compare;
845         bool reverse = OPT_GIVEN(LST, REVERSE);
846
847         tagtab = hash_tags(NULL, NULL);
848         for (
849                 liter = linhash_iterator_new(tagtab, comp, reverse);
850                 (itemp = linhash_iterator_item(liter));
851                 linhash_iterator_next(liter)
852         ) {
853                 if (OPT_GIVEN(LST, LONG))
854                         printf("%lu\t%s\n", (long unsigned)itemp->object,
855                                 itemp->key);
856                 else
857                         printf("%s\n", itemp->key);
858                 free((char *)itemp->key);
859         }
860         linhash_iterator_free(liter);
861         linhash_free(tagtab);
862         return 0;
863 }
864 EXPORT_CMD_HANDLER(lst);
865
866 static int com_stats(void)
867 {
868         struct linhash_table *tagtab;
869         struct linhash_iterator *liter;
870         struct linhash_item *itemp;
871         unsigned num_epi_files, num_epis, num_unique_tags, num_x = 0;
872         long unsigned num_tags = 0;
873         char *xdir;
874         struct regfile_iter *riter;
875
876         tagtab = hash_tags(&num_epi_files, &num_epis);
877         for (
878                 liter = linhash_iterator_new(tagtab, NULL, false);
879                 (itemp = linhash_iterator_item(liter));
880                 linhash_iterator_next(liter)
881         ) {
882                 free((char *)itemp->key);
883                 num_tags += (long unsigned)itemp->object;
884         }
885         num_unique_tags = linhash_num_items(tagtab);
886         linhash_iterator_free(liter);
887         xdir = get_xdir();
888         for (
889                 regfile_iter_new(xdir, &riter);
890                 regfile_iter_basename(riter);
891                 regfile_iter_next(riter)
892         )
893                 num_x++;
894         regfile_iter_free(riter);
895         free(xdir);
896         printf("number of tag expressions.......... %5u\n", num_x);
897         printf("number of epigram files............ %5u\n", num_epi_files);
898         printf("number of epigrams................. %5u\n", num_epis);
899         printf("number of tags..................... %5lu\n", num_tags);
900         printf("number of unique tags.............. %5u\n", num_unique_tags);
901         printf("average number of epigrams per file %8.02f\n",
902                 num_epi_files > 0?  (float)num_epis / num_epi_files : 0);
903         printf("average number of tags per epigram. %8.02f\n", num_epis > 0?
904                 (float)num_tags / num_epis : 0);
905         printf("average number of tag recurrence... %8.02f\n", num_unique_tags > 0?
906                 (float)num_tags / num_unique_tags : 0);
907         if (OPT_GIVEN(STATS, VERBOSE)) {
908                 char *lh_stats = linhash_statistics(tagtab);
909                 printf("\nlinear hashing statistics:\n%s\n", lh_stats);
910                 free(lh_stats);
911         }
912         linhash_free(tagtab);
913         return 1;
914 }
915 EXPORT_CMD_HANDLER(stats);
916
917 #define LSG_TFORTUNE_CMD(_name) #_name
918 static const char * const subcommand_names[] = {LSG_TFORTUNE_SUBCOMMANDS NULL};
919 #undef LSG_TFORTUNE_CMD
920
921 static void show_subcommand_summary(bool verbose)
922 {
923         int i;
924
925         printf("Available subcommands:\n");
926         if (verbose) {
927                 const struct lls_command *cmd;
928                 for (i = 1; (cmd = lls_cmd(i, tfortune_suite)); i++) {
929                         const char *purpose = lls_purpose(cmd);
930                         const char *name = lls_command_name(cmd);
931                         printf("%-11s%s\n", name, purpose);
932                 }
933         } else {
934                 unsigned n = 8;
935                 printf("\t");
936                 for (i = 0; i < LSG_NUM_TFORTUNE_SUBCOMMANDS; i++) {
937                         if (i > 0)
938                                 n += printf(", ");
939                         if (n > 70) {
940                                 printf("\n\t");
941                                 n = 8;
942                         }
943                         n += printf("%s", subcommand_names[i]);
944                 }
945                 printf("\n");
946         }
947 }
948
949 static int com_help(void)
950 {
951         int ret;
952         char *errctx, *help;
953         const char *arg;
954         const struct lls_command *cmd;
955
956         ret = lls_check_arg_count(sublpr, 0, 1, &errctx);
957         if (ret < 0)
958                 return lopsub_error(ret, &errctx);
959         if (lls_num_inputs(sublpr) == 0) {
960                 show_subcommand_summary(OPT_GIVEN(HELP, LONG));
961                 return 0;
962         }
963         arg = lls_input(0, sublpr);
964         ret = lls_lookup_subcmd(arg, tfortune_suite, &errctx);
965         if (ret < 0)
966                 return lopsub_error(ret, &errctx);
967         cmd = lls_cmd(ret, tfortune_suite);
968         if (OPT_GIVEN(HELP, LONG))
969                 help = lls_long_help(cmd);
970         else
971                 help = lls_short_help(cmd);
972         printf("%s\n", help);
973         free(help);
974         return 1;
975 }
976 EXPORT_CMD_HANDLER(help);
977
978 const char *GET_VERSION(void);
979 static void handle_help_and_version(void)
980 {
981         int i;
982         char *help;
983         const struct lls_command *cmd;
984
985         if (OPT_GIVEN(TFORTUNE, VERSION)) {
986                 printf(PACKAGE " %s\n"
987                         "Copyright (C) " COPYRIGHT_YEAR " " AUTHOR ".\n"
988                         "License: " LICENSE ": <" LICENSE_URL ">.\n"
989                         "This is free software: you are free to change and redistribute it.\n"
990                         "There is NO WARRANTY, to the extent permitted by law.\n"
991                         "\n"
992                         "Web page: " PACKAGE_HOMEPAGE "\n"
993                         "Clone URL: " CLONE_URL "\n"
994                         "Gitweb: " GITWEB_URL "\n"
995                         "Author's Home Page: " HOME_URL "\n"
996                         "Send feedback to: " AUTHOR " <" EMAIL ">\n"
997                         ,
998                         GET_VERSION()
999                 );
1000                 exit(EXIT_SUCCESS);
1001         }
1002         cmd = CMD_PTR(TFORTUNE);
1003         if (OPT_GIVEN(TFORTUNE, DETAILED_HELP))
1004                 help = lls_long_help(cmd);
1005         else if (OPT_GIVEN(TFORTUNE, HELP))
1006                 help = lls_short_help(cmd);
1007         else
1008                 return;
1009         printf("%s\n", help);
1010         free(help);
1011         if (OPT_GIVEN(TFORTUNE, DETAILED_HELP))
1012                 for (i = 1; (cmd = lls_cmd(i, tfortune_suite)); i++) {
1013                         help = lls_short_help(cmd);
1014                         printf("%s\n---\n", help);
1015                         free(help);
1016                 }
1017         else
1018                 show_subcommand_summary(true /* verbose */);
1019         exit(EXIT_SUCCESS);
1020 }
1021
1022 enum tf_word_type {
1023         WT_COMMAND_NAME,
1024         WT_DOUBLE_DASH, /* -- */
1025         WT_SHORT_OPT_WITH_ARG, /* -l */
1026         WT_SHORT_OPT_WITHOUT_ARG, /* -V, -abc=d */
1027         WT_LONG_OPT_WITH_ARG, /* --loglevel */
1028         WT_LONG_OPT_WITHOUT_ARG, /* --foo=bar --help */
1029         WT_OPTION_ARG,
1030         WT_NON_OPTION_ARG,
1031         WT_DUNNO,
1032 };
1033
1034 static bool is_short_opt(const char *word)
1035 {
1036         if (word[0] != '-')
1037                 return false;
1038         if (word[1] == '-')
1039                 return false;
1040         if (word[1] == '\0')
1041                 return false;
1042         return true;
1043 }
1044
1045 static bool is_long_opt(const char *word)
1046 {
1047         if (word[0] != '-')
1048                 return false;
1049         if (word[1] != '-')
1050                 return false;
1051         if (word[2] == '\0')
1052                 return false;
1053         return true;
1054 }
1055
1056 /* whether the next word will be an arg to this short opt */
1057 static int short_opt_needs_arg(const char *word,
1058                 const char * const *short_opts)
1059 {
1060         size_t n, len;
1061
1062         if (strchr(word, '='))
1063                 return false;
1064         len = strlen(word);
1065         for (n = 0; short_opts[n]; n++) {
1066                 const char *opt = short_opts[n];
1067                 if (word[len - 1] != opt[1])
1068                         continue;
1069                 if (opt[2] == '=')
1070                         return true;
1071                 else
1072                         return false;
1073         }
1074         return -1;
1075 }
1076
1077 /* whether the next word will be an arg to this long opt */
1078 static int long_opt_needs_arg(const char *word,
1079                 const char * const *long_opts)
1080 {
1081         size_t n;
1082
1083         if (strchr(word, '='))
1084                 return false;
1085         for (n = 0; long_opts[n]; n++) {
1086                 const char *opt = long_opts[n];
1087                 size_t len = strlen(opt);
1088
1089                 if (opt[len - 1] == '=')
1090                         len--;
1091                 if (strncmp(word + 2, opt + 2, len - 2))
1092                         continue;
1093                 if (opt[len] == '=')
1094                         return true;
1095                 else
1096                         return false;
1097         }
1098         return -1;
1099 }
1100
1101 static bool get_word_types(unsigned cword, unsigned arg0,
1102                 const char * const *short_opts, const char * const *long_opts,
1103                 enum tf_word_type *result)
1104 {
1105         const char *word;
1106         unsigned n;
1107         bool have_dd = false;
1108         int ret;
1109
1110         /* index zero is always the command name */
1111         assert(cword > arg0);
1112         result[arg0] = WT_COMMAND_NAME;
1113         for (n = arg0 + 1; n < cword; n++) {
1114                 enum tf_word_type prev_type = result[n - 1];
1115
1116                 if (have_dd) {
1117                         result[n] = WT_NON_OPTION_ARG;
1118                         continue;
1119                 }
1120                 if (prev_type == WT_SHORT_OPT_WITH_ARG) {
1121                         result[n] = WT_OPTION_ARG;
1122                         continue;
1123                 }
1124                 if (prev_type == WT_LONG_OPT_WITH_ARG) {
1125                         result[n] = WT_OPTION_ARG;
1126                         continue;
1127                 }
1128                 word = lls_input(n, sublpr);
1129                 if (strcmp(word, "--") == 0) {
1130                         result[n] = WT_DOUBLE_DASH;
1131                         have_dd = true;
1132                         continue;
1133                 }
1134                 if (is_short_opt(word)) {
1135                         ret = short_opt_needs_arg(word, short_opts);
1136                         if (ret < 0)
1137                                 goto dunno;
1138                         if (ret > 0)
1139                                 result[n] = WT_SHORT_OPT_WITH_ARG;
1140                         else
1141                                 result[n] = WT_SHORT_OPT_WITHOUT_ARG;
1142                         continue;
1143                 }
1144                 if (is_long_opt(word)) {
1145                         ret = long_opt_needs_arg(word, long_opts);
1146                         if (ret < 0)
1147                                 goto dunno;
1148                         if (ret > 0)
1149                                 result[n] = WT_LONG_OPT_WITH_ARG;
1150                         else
1151                                 result[n] = WT_LONG_OPT_WITHOUT_ARG;
1152                         continue;
1153                 }
1154                 result[n] = WT_NON_OPTION_ARG;
1155         }
1156         return have_dd;
1157 dunno:
1158         for (; n <= cword; n++)
1159                 result[n] = WT_DUNNO;
1160         return false;
1161 }
1162
1163 #define DUMMY_COMPLETER(_name) static char **complete_ ## _name( \
1164         __attribute__ ((unused)) uint32_t cword, \
1165         __attribute__ ((unused)) unsigned arg0, \
1166         __attribute__ ((unused)) bool have_dd \
1167         ) {return NULL;}
1168
1169 DUMMY_COMPLETER(tfortune)
1170 DUMMY_COMPLETER(compgen)
1171 DUMMY_COMPLETER(completer)
1172
1173 static const char * const supercmd_opts[] = {LSG_TFORTUNE_TFORTUNE_OPTS, NULL};
1174
1175 static void print_zero_terminated_list(const char * const *list)
1176 {
1177         const char * const *c;
1178         for (c = list; *c; c++)
1179                 printf("%s%c", *c, '\0');
1180 }
1181
1182 static void print_option_list(const char * const *opts)
1183 {
1184         const char * const *c;
1185
1186         for (c = opts; *c; c++) {
1187                 int len = strlen(*c);
1188                 assert(len > 0);
1189                 if ((*c)[len - 1] == '=')
1190                         len--;
1191                 printf("%.*s%c", len, *c, '\0');
1192         }
1193 }
1194
1195 static void activate_dirname_completion(void)
1196 {
1197         printf("%c", '\0');
1198         printf("-o dirnames%c", '\0');
1199 }
1200
1201 static void complete_loglevels(void)
1202 {
1203         unsigned n;
1204         const struct lls_option *opt = lls_opt(LSG_TFORTUNE_TFORTUNE_OPT_LOGLEVEL,
1205                 CMD_PTR(TFORTUNE));
1206
1207         for (n = 0; n < LSG_NUM_TFORTUNE_TFORTUNE_LOGLEVEL_VALUES; n++) {
1208                 const char *v = lls_enum_string_val(n, opt);
1209                 printf("%s%c", v, '\0');
1210         }
1211 }
1212
1213 static char **complete_dentries(const char *dir)
1214 {
1215         const char *bn;
1216         struct regfile_iter *riter;
1217         unsigned n;
1218         char **result = NULL;
1219
1220         regfile_iter_new(dir, &riter);
1221         for (
1222                 n = 0;
1223                 (bn = regfile_iter_basename(riter));
1224                 regfile_iter_next(riter), n++
1225         ) {
1226                 result = xrealloc(result, (n + 2) * sizeof(*result));
1227                 result[n] = xstrdup(bn);
1228                 result[n + 1] = NULL;
1229         }
1230         return result;
1231 }
1232
1233 static char **complete_ede(__attribute__ ((unused)) uint32_t cword,
1234                 __attribute__ ((unused)) unsigned arg0,
1235                 __attribute__ ((unused)) bool have_dd)
1236 {
1237         char **result, *epidir = get_epidir();
1238
1239         result = complete_dentries(epidir);
1240         free(epidir);
1241         return result;
1242 }
1243
1244 static char **complete_edx(__attribute__ ((unused)) uint32_t cword,
1245                 __attribute__ ((unused)) unsigned arg0,
1246                 __attribute__ ((unused)) bool have_dd)
1247 {
1248         char **result, *xdir = get_xdir();
1249
1250         result = complete_dentries(xdir);
1251         free(xdir);
1252         return result;
1253 }
1254
1255 static char **complete_std_opts(bool have_dd, const char * const *opts)
1256 {
1257         if (have_dd)
1258                 print_option_list(opts);
1259         else
1260                 print_option_list(supercmd_opts);
1261         return NULL;
1262 }
1263
1264 static char **complete_stats(__attribute__ ((unused)) uint32_t cword,
1265                 __attribute__ ((unused)) unsigned arg0, bool have_dd)
1266 {
1267         const char * const opts[] = {LSG_TFORTUNE_STATS_OPTS, NULL};
1268         return complete_std_opts(have_dd, opts);
1269 }
1270
1271 static char **complete_lse(__attribute__ ((unused)) uint32_t cword,
1272                 __attribute__ ((unused)) unsigned arg0, bool have_dd)
1273 {
1274         const char * const opts[] = {LSG_TFORTUNE_LSE_OPTS, NULL};
1275         return complete_std_opts(have_dd, opts);
1276 }
1277
1278 static char **complete_lst(__attribute__ ((unused)) uint32_t cword,
1279                 __attribute__ ((unused)) unsigned arg0, bool have_dd)
1280 {
1281         const char * const opts[] = {LSG_TFORTUNE_LST_OPTS, NULL};
1282         return complete_std_opts(have_dd, opts);
1283 }
1284
1285 static char **complete_lsx(__attribute__ ((unused)) uint32_t cword,
1286                 __attribute__ ((unused)) unsigned arg0, bool have_dd)
1287 {
1288         const char * const opts[] = {LSG_TFORTUNE_LSX_OPTS, NULL};
1289         return complete_std_opts(have_dd, opts);
1290 }
1291
1292 static char **complete_help(__attribute__ ((unused)) uint32_t cword,
1293                 __attribute__ ((unused)) unsigned arg0, bool have_dd)
1294 {
1295         const char * const opts[] = {LSG_TFORTUNE_HELP_OPTS, NULL};
1296
1297         if (!have_dd)
1298                 print_option_list(supercmd_opts);
1299         else
1300                 print_option_list(opts);
1301         print_zero_terminated_list(subcommand_names);
1302         return NULL;
1303 }
1304
1305 static char **complete_print(uint32_t cword, unsigned arg0, bool have_dd)
1306 {
1307         const char * const short_opts[] = {LSG_TFORTUNE_PRINT_SHORT_OPTS, NULL};
1308         const char * const long_opts[] = {LSG_TFORTUNE_PRINT_LONG_OPTS, NULL};
1309         const char * const opts[] = {LSG_TFORTUNE_PRINT_OPTS, NULL};
1310         enum tf_word_type *word_types, prev_type;
1311         const char *prev;
1312         char **result, *xdir;
1313
1314         word_types = xmalloc(cword * sizeof(*word_types));
1315         get_word_types(cword, arg0, short_opts, long_opts, word_types);
1316         prev = lls_input(cword - 1, sublpr);
1317         prev_type = word_types[cword - 1];
1318         free(word_types);
1319         switch (prev_type) {
1320                 case WT_COMMAND_NAME:
1321                 case WT_SHORT_OPT_WITHOUT_ARG:
1322                 case WT_OPTION_ARG:
1323                 case WT_LONG_OPT_WITHOUT_ARG:
1324                 case WT_DOUBLE_DASH:
1325                         if (!have_dd)
1326                                 print_option_list(supercmd_opts);
1327                         else
1328                                 print_option_list(opts);
1329                         return NULL;
1330                 case WT_SHORT_OPT_WITH_ARG:
1331                         if (strcmp(prev, "-x") == 0)
1332                                 goto complete_expression;
1333                         break;
1334                 case WT_LONG_OPT_WITH_ARG:
1335                         if (strcmp(prev, "--expression") == 0)
1336                                 goto complete_expression;
1337                         break;
1338                 default:
1339                         return NULL;
1340         }
1341 complete_expression:
1342         xdir = get_xdir();
1343         result = complete_dentries(xdir);
1344         free(xdir);
1345         return result;
1346 }
1347
1348 typedef char **(*completer)(uint32_t cword, unsigned arg0, bool have_dd);
1349
1350 #define LSG_TFORTUNE_CMD(_name) complete_ ## _name
1351 static const completer completers[] = {LSG_TFORTUNE_COMMANDS};
1352 #undef LSG_TFORTUNE_CMD
1353
1354 static int call_subcmd_completer(unsigned cmd_num, int arg0, uint32_t cword,
1355                 bool have_dd)
1356 {
1357         char **c, **candidates = completers[cmd_num](cword, arg0, have_dd);
1358
1359         if (!candidates)
1360                 return 0;
1361         for (c = candidates; *c; c++) {
1362                 printf("%s%c", *c, '\0');
1363                 free(*c);
1364         }
1365         free(candidates);
1366         return 1;
1367 }
1368
1369 static bool need_subcommand_completer(uint32_t cword, unsigned subcmd_idx,
1370                 const enum tf_word_type *word_types, bool have_dd)
1371 {
1372         enum tf_word_type prev_type;
1373         const char *word;
1374
1375         if (subcmd_idx == 0)
1376                 return false;
1377         if (have_dd)
1378                 return true;
1379         prev_type = word_types[cword - 1];
1380         assert(prev_type != WT_COMMAND_NAME);
1381         switch (prev_type) {
1382                 case WT_SHORT_OPT_WITH_ARG:
1383                 case WT_LONG_OPT_WITH_ARG:
1384                 case WT_DUNNO:
1385                         return false;
1386                 default:
1387                         break;
1388         }
1389         word = lls_input(cword, sublpr);
1390         if (is_short_opt(word))
1391                 return false;
1392         if (is_long_opt(word))
1393                 return false;
1394         return true;
1395 }
1396
1397 static int com_compgen(void)
1398 {
1399         unsigned n;
1400         uint32_t cword = OPT_UINT32_VAL(COMPGEN, CURRENT_WORD_INDEX);
1401         int ret;
1402         unsigned subcmd_idx;
1403         const char *word, *prev;
1404         const char * const short_opts[] = {LSG_TFORTUNE_TFORTUNE_SHORT_OPTS, NULL};
1405         const char * const long_opts[] = {LSG_TFORTUNE_TFORTUNE_LONG_OPTS, NULL};
1406         enum tf_word_type *word_types, prev_type;
1407         bool have_dd;
1408
1409         if (cword == 0 || cword > lls_num_inputs(sublpr)) {
1410                 ERROR_LOG("current word index == %u!?\n", cword);
1411                 return -ERRNO_TO_TF_ERROR(EINVAL);
1412         }
1413         word_types = xmalloc(cword * sizeof(*word_types));
1414         have_dd = get_word_types(cword, 0, short_opts, long_opts, word_types);
1415         /*
1416          * Locate the subcommand argument, if present. It is always the first
1417          * non-option argument.
1418          */
1419         subcmd_idx = 0;
1420         for (n = 1; n < cword; n++) {
1421                 if (word_types[n] != WT_NON_OPTION_ARG)
1422                         continue;
1423                 subcmd_idx = n;
1424                 break;
1425         }
1426         if (need_subcommand_completer(cword, subcmd_idx, word_types, have_dd)) {
1427                 free(word_types);
1428                 word = lls_input(subcmd_idx, sublpr);
1429                 ret = lls_lookup_subcmd(word, tfortune_suite, NULL);
1430                 if (ret < 0) /* invalid subcommand */
1431                         return 0;
1432                 return call_subcmd_completer(ret, subcmd_idx, cword, have_dd);
1433         }
1434         /* no subcommand */
1435         prev_type = word_types[cword - 1];
1436         prev = lls_input(cword - 1, sublpr);
1437         free(word_types);
1438         switch (prev_type) {
1439                 case WT_DUNNO:
1440                         return 0;
1441                 case WT_COMMAND_NAME:
1442                 case WT_SHORT_OPT_WITHOUT_ARG:
1443                 case WT_OPTION_ARG:
1444                 case WT_NON_OPTION_ARG:
1445                 case WT_LONG_OPT_WITHOUT_ARG:
1446                         if (!have_dd)
1447                                 print_option_list(supercmd_opts);
1448                         /* fall through */
1449                 case WT_DOUBLE_DASH:
1450                         print_zero_terminated_list(subcommand_names);
1451                         break;
1452                 case WT_SHORT_OPT_WITH_ARG:
1453                         if (strcmp(prev, "-b") == 0) {
1454                                 activate_dirname_completion();
1455                                 return 1;
1456                         }
1457                         if (strcmp(prev, "-l") == 0) {
1458                                 complete_loglevels();
1459                                 return 1;
1460                         }
1461                         break;
1462                 case WT_LONG_OPT_WITH_ARG:
1463                         if (strcmp(prev, "--basedir") == 0) {
1464                                 activate_dirname_completion();
1465                                 return 1;
1466                         }
1467                         if (strcmp(prev, "--loglevel") == 0) {
1468                                 complete_loglevels();
1469                                 return 1;
1470                         }
1471                         break;
1472         }
1473         return 0;
1474 }
1475 EXPORT_CMD_HANDLER(compgen);
1476
1477 static int com_completer(void)
1478 {
1479         printf("%s\n",
1480                 "_tfortune() \n"
1481                 "{ \n"
1482                         "local -i i offset=${TF_OFFSET:-0} \n"
1483                         "local w compopts= have_empty=false\n"
1484                         "local cur=\"${COMP_WORDS[$COMP_CWORD]}\" \n"
1485
1486                         "i=0 \n"
1487                         "COMPREPLY=() \n"
1488                         "while read -d '' w; do \n"
1489                                 "[[ -z \"$w\" ]] && { have_empty=true; continue; }\n"
1490                                 "if [[ $have_empty == true ]]; then\n"
1491                                         "compopt $w\n"
1492                                 "else \n"
1493                                         "[[ \"$w\" != \"$cur\"* ]] && continue \n"
1494                                         "COMPREPLY[i]=\"$w\" \n"
1495                                         "let i++ \n"
1496                                 "fi \n"
1497                         "done < <(tfortune -- compgen --current-word-index \\\n"
1498                         "\"$((COMP_CWORD + offset))\" -- $TF_EXTRA \"${COMP_WORDS[@]}\")\n"
1499                 "} \n"
1500                 "complete -F _tfortune tfortune \n"
1501         );
1502         if (OPT_GIVEN(COMPLETER, ALIAS)) {
1503                 const char *ali = OPT_STRING_VAL(PRINT, EXPRESSION);
1504                 printf("alias %s=\"tfortune --\"\n", ali);
1505                 printf("_%s() { \n"
1506                                 "COMP_WORDS[0]='--'\n"
1507                                 "TF_EXTRA='tf' \n"
1508                                 "TF_OFFSET=1 \n"
1509                                 "_tfortune \"$@\" \n"
1510                                 "unset TF_EXTRA TF_OFFSET\n"
1511                         "}\n",
1512                         ali
1513                 );
1514                 printf("complete -F _%s %s \n", ali, ali);
1515         }
1516         return 1;
1517 }
1518 EXPORT_CMD_HANDLER(completer);
1519
1520 int main(int argc, char **argv)
1521 {
1522         char *errctx;
1523         int ret;
1524         const struct lls_command *cmd = CMD_PTR(TFORTUNE), *subcmd;
1525         const struct tf_user_data *ud;
1526         unsigned num_inputs;
1527
1528         ret = lls_parse(argc, argv, cmd, &lpr, &errctx);
1529         if (ret < 0) {
1530                 lopsub_error(ret, &errctx);
1531                 exit(EXIT_FAILURE);
1532         }
1533         loglevel_arg_val = OPT_UINT32_VAL(TFORTUNE, LOGLEVEL);
1534         handle_help_and_version();
1535         num_inputs = lls_num_inputs(lpr);
1536         if (num_inputs == 0) {
1537                 show_subcommand_summary(true /* verbose */);
1538                 ret = 0;
1539                 goto free_lpr;
1540         }
1541         ret = lls_lookup_subcmd(argv[argc - num_inputs], tfortune_suite, &errctx);
1542         if (ret < 0) {
1543                 ret = lopsub_error(ret, &errctx);
1544                 goto free_lpr;
1545         }
1546         subcmd = lls_cmd(ret, tfortune_suite);
1547         ret = lls_parse(num_inputs, argv + argc - num_inputs, subcmd,
1548                 &sublpr, &errctx);
1549         if (ret < 0) {
1550                 ret = lopsub_error(ret, &errctx);
1551                 goto free_lpr;
1552         }
1553         ud = lls_user_data(subcmd);
1554         ret = ud->handler();
1555         lls_free_parse_result(sublpr, subcmd);
1556         if (ret < 0)
1557                 ERROR_LOG("%s\n", tf_strerror(-ret));
1558 free_lpr:
1559         lls_free_parse_result(lpr, cmd);
1560         exit(ret >= 0? EXIT_SUCCESS : EXIT_FAILURE);
1561 }