com_locate()
authorAndre Noll <maan@tuebingen.mpg.de>
Sun, 13 May 2018 16:19:45 +0000 (18:19 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Tue, 21 May 2019 10:04:39 +0000 (12:04 +0200)
flags n and i are specified within the command line arg

implements --edit

More complicated than expected, because

* need to track basenames
* epi iter needed to learn how to skip the rest of the file

tfortune.c
tfortune.suite.m4

index 186b2cc..0a61b52 100644 (file)
@@ -231,6 +231,7 @@ static int tx2ast(const struct iovec *tx, struct txp_context **ast)
 }
 
 struct epi_iter {
+       char **basenames;
        struct iovec *maps;
        unsigned num_maps;
        unsigned map_num;
@@ -238,15 +239,17 @@ struct epi_iter {
        unsigned num_epis;
 };
 
-static bool get_next_epi(struct epi_iter *eiter)
+static bool get_next_epi(struct epi_iter *eiter, bool skip)
 {
        const char *epi_start = NULL;
 
+       if (skip)
+               eiter->map_num++;
        for (; eiter->map_num < eiter->num_maps; eiter->map_num++) {
                struct iovec *iov = eiter->maps + eiter->map_num;
                const char *buf, *end = iov->iov_base + iov->iov_len;
 
-               if (!epi_start && eiter->epi.tags.base)
+               if (!epi_start && !skip && eiter->epi.tags.base)
                        epi_start = eiter->epi.tags.base
                                + eiter->epi.tags.len + 1;
                else
@@ -316,10 +319,10 @@ static char *get_xdir(void)
        return xdir;
 }
 
-static struct epi_iter *epi_iter_new(void)
+static struct epi_iter *epi_iter_new(const struct lls_parse_result *parse_result)
 {
        struct epi_iter *eiter = xmalloc(sizeof(*eiter));
-       unsigned num_inputs = lls_num_inputs(sublpr);
+       unsigned num_inputs = parse_result? lls_num_inputs(parse_result) : 0;
 
        if (num_inputs == 0) {
                struct regfile_iter *riter;
@@ -329,6 +332,7 @@ static struct epi_iter *epi_iter_new(void)
                regfile_iter_new(epidir, &riter);
                free(epidir);
                eiter->maps = NULL;
+               eiter->basenames = NULL;
                eiter->num_maps = 0;
                for (;
                        regfile_iter_map(riter, &iov);
@@ -337,14 +341,22 @@ static struct epi_iter *epi_iter_new(void)
                        eiter->num_maps++;
                        eiter->maps = xrealloc(eiter->maps,
                                eiter->num_maps * sizeof(*eiter->maps));
+                       eiter->basenames = xrealloc(eiter->basenames,
+                               eiter->num_maps * sizeof(char *));
                        eiter->maps[eiter->num_maps - 1] = iov;
+                       eiter->basenames[eiter->num_maps - 1]
+                               = xstrdup(regfile_iter_basename(riter));
                }
                regfile_iter_free(riter);
        } else {
                unsigned n;
                eiter->maps = xmalloc(num_inputs * sizeof(*eiter->maps));
-               for (n = 0; n < num_inputs; n++)
-                       mmap_file(lls_input(n, sublpr), eiter->maps + n);
+               eiter->basenames = xmalloc(num_inputs * sizeof(char *));
+               for (n = 0; n < num_inputs; n++) {
+                       const char *arg = lls_input(n, sublpr);
+                       eiter->basenames[n] = xstrdup(arg);
+                       mmap_file(arg, eiter->maps + n);
+               }
                eiter->num_maps = num_inputs;
        }
        eiter->map_num = 0;
@@ -353,7 +365,7 @@ static struct epi_iter *epi_iter_new(void)
        eiter->epi.tags.base = NULL;
        eiter->epi.tags.len = 0;
        eiter->num_epis = 0;
-       get_next_epi(eiter);
+       get_next_epi(eiter, false /* do not skip */);
        return eiter;
 }
 
@@ -363,6 +375,11 @@ static const struct epigram *epi_iter_get(const struct epi_iter *eiter)
                &eiter->epi : NULL;
 }
 
+static const char *epi_iter_basename(const struct epi_iter *eiter)
+{
+       return eiter->basenames[eiter->map_num];
+}
+
 static unsigned epi_iter_num_maps(const struct epi_iter *eiter)
 {
        return eiter->num_maps;
@@ -373,9 +390,9 @@ static unsigned epi_iter_num_epis(const struct epi_iter *eiter)
        return eiter->num_epis;
 }
 
-static void epi_iter_next(struct epi_iter *eiter)
+static void epi_iter_next(struct epi_iter *eiter, bool skip)
 {
-       get_next_epi(eiter);
+       get_next_epi(eiter, skip);
 }
 
 static void epi_iter_free(struct epi_iter *eiter)
@@ -387,6 +404,7 @@ static void epi_iter_free(struct epi_iter *eiter)
        for (n = 0; n < eiter->num_maps; n++)
                munmap(eiter->maps[n].iov_base, eiter->maps[n].iov_len);
        free(eiter->maps);
+       free(eiter->basenames);
        free(eiter);
 }
 
@@ -407,9 +425,9 @@ static int com_print(void)
        if (ret < 0)
                goto free_tx;
        for (
-               eiter = epi_iter_new();
+               eiter = epi_iter_new(sublpr);
                (epi = epi_iter_get(eiter));
-               epi_iter_next(eiter)
+               epi_iter_next(eiter, false /* do not skip */)
        ) {
                if (!epi_admissible(epi, ast))
                        continue;
@@ -463,6 +481,65 @@ static void open_editor(char **argv)
        _exit(EXIT_FAILURE);
 }
 
+static int com_locate(void)
+{
+       int ret, argc;
+       struct iovec tx;
+       struct txp_context *ast;
+       struct epi_iter *eiter;
+       const struct epigram *epi;
+       char *errctx;
+       const char *arg;
+       bool skip;
+       char **argv = NULL;
+       char *epidir = NULL;
+       bool edit = OPT_GIVEN(LOCATE, EDIT);
+
+       ret = lls_check_arg_count(sublpr, 1, 1, &errctx);
+       if (ret < 0)
+               return lopsub_error(ret, &errctx);
+       arg = lls_input(0, sublpr);
+       tx.iov_len = xasprintf((char **)&tx.iov_base, "text =~ %s", arg);
+       ret = tx2ast(&tx, &ast);
+       if (ret < 0)
+               goto free_tx;
+       if (edit) {
+               argc = 1;
+               argv = xmalloc((argc + 1) * sizeof(char *));
+               epidir = get_epidir();
+       }
+       for (
+               eiter = epi_iter_new(NULL);
+               (skip = false, epi = epi_iter_get(eiter));
+               epi_iter_next(eiter, skip)
+       ) {
+               if (!epi_admissible(epi, ast))
+                       continue;
+               skip = true;
+               if (!edit) {
+                       printf("%s\n", epi_iter_basename(eiter));
+                       continue;
+               }
+               argc++;
+               argv = xrealloc(argv, (argc + 1) * sizeof(char *));
+               xasprintf(&argv[argc - 1], "%s/%s", epidir,
+                       xstrdup(epi_iter_basename(eiter)));
+       }
+       epi_iter_free(eiter);
+       txp_free(ast);
+       if (!edit) {
+               ret = 0;
+               goto free_tx;
+       }
+       argv[argc] = NULL;
+       open_editor(argv);
+       ret = 1;
+free_tx:
+       free(tx.iov_base);
+       return ret;
+}
+EXPORT_CMD_HANDLER(locate);
+
 static int create_dir(const char *path)
 {
        int ret;
@@ -792,9 +869,9 @@ static struct linhash_table *hash_tags(unsigned *num_epi_files,
        const struct epigram *epi;
 
        for (
-               eiter = epi_iter_new();
+               eiter = epi_iter_new(sublpr);
                (epi = epi_iter_get(eiter));
-               epi_iter_next(eiter)
+               epi_iter_next(eiter, false /* do not skip */)
        ) {
                struct tag_iter *titer;
                const char *tag;
@@ -1259,6 +1336,13 @@ static char **complete_stats(__attribute__ ((unused)) uint32_t cword,
        return complete_std_opts(have_dd, opts);
 }
 
+static char **complete_locate(__attribute__ ((unused)) uint32_t cword,
+               __attribute__ ((unused)) unsigned arg0,
+               __attribute__ ((unused)) bool have_dd)
+{
+       return NULL;
+}
+
 static char **complete_lse(__attribute__ ((unused)) uint32_t cword,
                __attribute__ ((unused)) unsigned arg0, bool have_dd)
 {
index 91d00c3..c0c5ce4 100644 (file)
@@ -159,6 +159,26 @@ caption = Subcommands
                purpose of each command.
        [/help]
 
+[subcommand locate]
+       purpose = identify epigram files which contain epigrams matching a pattern
+       non-opts-name = <regex>
+       [description]
+               This prints the names of the epigram files which contain at least one
+               epigram that matches the given pattern. It is similar to running the
+               shell command "grep -El <regex>" on all epigram files.
+
+               The <regex> argument must be of the form described in section "Tag
+               Expressions" of tfortune(1).
+       [/description]
+       [option edit]
+               short_opt = e
+               summary = edit matching files
+               [help]
+                       Instead of printing file names, all epigram files are opened in
+                       the editor which contain at least one matching epigram.  See the
+                       description of the "ede" subcommand for how the editor is invoked.
+               [/help]
+
 [subcommand lse]
        purpose = list epigram files
        [description]