commit:     c5aba3a0bd055688120dbabb9c3826ed46ffc795
Author:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
AuthorDate: Sun Apr 28 07:57:05 2019 +0000
Commit:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
CommitDate: Sun Apr 28 07:57:05 2019 +0000
URL:        https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=c5aba3a0

qgrep: rewrite using libq/vdb and libq/cache

(re)use the traversion logic from libq instead of re-implementing this.
Additional benefits are less code and usage of metadata when available.

Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org>

 applets.h                      |   2 +-
 man/include/qgrep.desc         |   9 +-
 man/include/qgrep.optdesc.yaml |   5 +-
 man/q.1                        |   2 +-
 man/qgrep.1                    |  16 +-
 qgrep.c                        | 756 ++++++++++++++++++++++-------------------
 6 files changed, 429 insertions(+), 361 deletions(-)

diff --git a/applets.h b/applets.h
index 5889de6..4dd5bc2 100644
--- a/applets.h
+++ b/applets.h
@@ -73,7 +73,7 @@ static const struct applet_t {
        /*
        {"qglsa",     qglsa_main,     "<action> <list>", "check GLSAs against 
system"},
        */
-       {"qgrep",     qgrep_main,     "<misc args>",     "grep in ebuilds"},
+       {"qgrep",     qgrep_main,     "<expr> [pkg ...]", "grep in ebuilds"},
        {"qlist",     qlist_main,     "<pkgname>",       "list files owned by 
pkgname"},
        {"qlop",      qlop_main,      "<pkgname>",       "emerge log analyzer"},
        {"qmerge",    qmerge_main,    "<pkgnames>",      "fetch and merge 
binary package"},

diff --git a/man/include/qgrep.desc b/man/include/qgrep.desc
index c95d35a..98bcc09 100644
--- a/man/include/qgrep.desc
+++ b/man/include/qgrep.desc
@@ -1,3 +1,6 @@
-\fIqgrep\fR searches for a given pattern in all ebuilds of the current
-portage tree.  Optionally the search is in all eclasses, or just in the
-ebuilds that are currently installed.
+\fIqgrep\fR searches for a given expression in all ebuilds of the
+current portage tree and defined additional overlays.  Optionally the
+search is in all eclasses, or just in the ebuilds that are currently
+installed.  To narrow the search, multiple targets can be given using
+atom syntax.  In particular, the trailing slash (/) syntax can be used
+to match an entire category.  See also \fIqatom\fR(1).

diff --git a/man/include/qgrep.optdesc.yaml b/man/include/qgrep.optdesc.yaml
index 87b174e..ad3874d 100644
--- a/man/include/qgrep.optdesc.yaml
+++ b/man/include/qgrep.optdesc.yaml
@@ -1,5 +1,4 @@
 verbose: |
-    Print multiple matches per files.  When this option is given
-    multiple times, also linenumber are printed for matches next to file
-    names.
+    Prefix each matching line with filename (like \fB-H\fR).  When this
+    option is given multiple times, also linenumbers are printed.
 quiet: Ignored for compatibility with other qapplets.

diff --git a/man/q.1 b/man/q.1
index b3e985b..afba24a 100644
--- a/man/q.1
+++ b/man/q.1
@@ -41,7 +41,7 @@ Print version and exit.
    qcheck <pkgname>       : verify integrity of installed packages
  qdepends <pkgname>       : show dependency info
     qfile <filename>      : list all pkgs owning files
-    qgrep <misc args>     : grep in ebuilds
+    qgrep <expr> [pkg ...]: grep in ebuilds
     qlist <pkgname>       : list files owned by pkgname
      qlop <pkgname>       : emerge log analyzer
    qmerge <pkgnames>      : fetch and merge binary package

diff --git a/man/qgrep.1 b/man/qgrep.1
index 662122d..048e28e 100644
--- a/man/qgrep.1
+++ b/man/qgrep.1
@@ -4,11 +4,14 @@
 qgrep \- grep in ebuilds
 .SH SYNOPSIS
 .B qgrep
-\fI[opts] <misc args>\fR
+\fI[opts] <expr> [pkg ...]\fR
 .SH DESCRIPTION
-\fIqgrep\fR searches for a given pattern in all ebuilds of the current
-portage tree.  Optionally the search is in all eclasses, or just in the
-ebuilds that are currently installed.
+\fIqgrep\fR searches for a given expression in all ebuilds of the
+current portage tree and defined additional overlays.  Optionally the
+search is in all eclasses, or just in the ebuilds that are currently
+installed.  To narrow the search, multiple targets can be given using
+atom syntax.  In particular, the trailing slash (/) syntax can be used
+to match an entire category.  See also \fIqatom\fR(1).
 .SH OPTIONS
 .TP
 \fB\-I\fR, \fB\-\-invert\-match\fR
@@ -60,9 +63,8 @@ Print <arg> lines of trailing context.
 Set the ROOT env var.
 .TP
 \fB\-v\fR, \fB\-\-verbose\fR
-Print multiple matches per files.  When this option is given
-multiple times, also linenumber are printed for matches next to file
-names.
+Prefix each matching line with filename (like \fB-H\fR).  When this
+option is given multiple times, also linenumbers are printed.
 .TP
 \fB\-q\fR, \fB\-\-quiet\fR
 Ignored for compatibility with other qapplets.

diff --git a/qgrep.c b/qgrep.c
index 16bb4c1..3950c22 100644
--- a/qgrep.c
+++ b/qgrep.c
@@ -64,35 +64,6 @@ static const char * const qgrep_opts_help[] = {
 };
 #define qgrep_usage(ret) usage(ret, QGREP_FLAGS, qgrep_long_opts, 
qgrep_opts_help, NULL, lookup_applet_idx("qgrep"))
 
-static char
-qgrep_name_match(const char* name, const int argc, depend_atom** argv)
-{
-       depend_atom* atom;
-       int i;
-
-       if ((atom = atom_explode(name)) == NULL)
-               return 0;
-
-       for (i = 0; i < argc; i++) {
-               if (argv[i] == NULL)
-                       continue;
-               if (atom->CATEGORY && argv[i]->CATEGORY && *(argv[i]->CATEGORY)
-                               && strcmp(atom->CATEGORY, argv[i]->CATEGORY))
-                       continue;
-               if (atom->PN && argv[i]->PN && *(argv[i]->PN)
-                               && strcmp(atom->PN, argv[i]->PN))
-                       continue;
-               if (atom->PVR && argv[i]->PVR && *(argv[i]->PVR)
-                               && strcmp(atom->PVR, argv[i]->PVR))
-                       continue;
-               atom_implode(atom);
-               return 1;
-       }
-
-       atom_implode(atom);
-       return 0;
-}
-
 /* Circular list of line buffers for --before */
 typedef struct qgrep_buf {
        char valid;
@@ -221,91 +192,356 @@ qgrep_print_before_context(qgrep_buf_t *current, const 
char num_lines_before,
        }
 }
 
-/* Yield the path of one of the installed ebuilds (from VDB). */
-static char *
-get_next_installed_ebuild(
-               char *ebuild_path,
-               size_t ebuild_path_len,
-               DIR *vdb_dir,
-               struct dirent **cat_dirent_pt,
-               DIR **cat_dir_pt)
+struct qgrep_grepargs {
+       bool do_count:1;
+       bool do_regex:1;
+       bool do_list:1;
+       bool show_filename:1;
+       bool show_name:1;
+       bool skip_comments:1;
+       bool invert_list:1;
+       bool invert_match:1;
+       char *skip_pattern;
+       char num_lines_before;
+       char num_lines_after;
+       qgrep_buf_t *buf_list;
+       regex_t skip_preg;
+       regex_t preg;
+       const char *query;
+       QGREP_STR_FUNC strfunc;
+       depend_atom **include_atoms;
+       const char *portdir;
+};
+
+static int
+qgrep_grepat(int fd, const char *file, const char *label,
+               struct qgrep_grepargs *a)
+{
+       FILE *newfp;
+       int need_separator = 0;
+       int count = 0;
+       int lineno = 0;
+       char remaining_after_context = 0;
+       char status = 1;
+       char *p;
+       bool per_file_output;
+
+       /* do we report results once per file or per line ? */
+       per_file_output =
+               a->do_count || (a->do_list && (!verbose || a->invert_list));
+
+       if (fd >= 0) {
+               int sfd = openat(fd, file, O_RDONLY|O_CLOEXEC);
+               newfp = sfd >= 0 ? fdopen(sfd, "r") : NULL;
+       } else {
+               newfp = fopen(file, "r");
+       }
+       if (newfp == NULL)
+               return status;
+
+       count = 0;
+       /* if there have been some matches already, then a
+        * separator will be needed */
+       need_separator =
+               !status && (a->num_lines_before || a->num_lines_after);
+       /* whatever is in the circular buffers list is no more a
+        * valid context */
+       qgrep_buf_list_invalidate(a->buf_list);
+
+       /* reading a new line always happen in the next buffer
+        * of the list */
+       while ((a->buf_list = a->buf_list->next) &&
+                       fgets(a->buf_list->buf, sizeof(a->buf_list->buf), 
newfp))
+       {
+               lineno++;
+               a->buf_list->valid = 1;
+
+               /* cleanup EOL */
+               if ((p = strrchr(a->buf_list->buf, '\n')) != NULL)
+                       *p = 0;
+               if ((p = strrchr(a->buf_list->buf, '\r')) != NULL)
+                       *p = 0;
+
+               if (a->skip_comments) {
+                       /* reject comments line ("^[ \t]*#") */
+                       p = a->buf_list->buf;
+                       while (*p == ' ' || *p == '\t') p++;
+                       if (*p == '#')
+                               goto print_after_context;
+               }
+
+               if (a->skip_pattern) {
+                       /* reject some other lines which match an
+                        * optional pattern */
+                       if (!a->do_regex) {
+                               if (a->strfunc(a->buf_list->buf, 
a->skip_pattern) != NULL)
+                                       goto print_after_context;
+                       } else {
+                               if (regexec(&a->skip_preg, a->buf_list->buf,
+                                                       0, NULL, 0) == 0)
+                                       goto print_after_context;
+                       }
+               }
+
+               /* four ways to match a line (with/without inversion
+                * and regexp) */
+               if (!a->invert_match) {
+                       if (a->do_regex == 0) {
+                               if (a->strfunc(a->buf_list->buf, a->query) == 
NULL)
+                                       goto print_after_context;
+                       } else {
+                               if (regexec(&a->preg, a->buf_list->buf, 0, 
NULL, 0) != 0)
+                                       goto print_after_context;
+                       }
+               } else {
+                       if (a->do_regex == 0) {
+                               if (a->strfunc(a->buf_list->buf, a->query) != 
NULL)
+                                       goto print_after_context;
+                       } else {
+                               if (regexec(&a->preg, a->buf_list->buf, 0, 
NULL, 0) == 0)
+                                       goto print_after_context;
+                       }
+               }
+
+               count++;
+               status = 0; /* got a match, exit status should be 0 */
+               if (per_file_output)
+                       continue;
+               /* matching files are listed out of this loop */
+
+               if ((need_separator > 0)
+                               && (a->num_lines_before || a->num_lines_after))
+                       printf("--\n");
+               /* "need_separator" is not a flag, but a counter, so that
+                * adjacent contextes are not separated */
+               need_separator = 0 - a->num_lines_before;
+               if (!a->do_list) {
+                       /* print the leading context */
+                       qgrep_print_before_context(a->buf_list,
+                                       a->num_lines_before, label,
+                                       ((verbose > 1) ? lineno : -1));
+                       /* print matching line */
+                       if (a->invert_match || *RED == '\0')
+                               qgrep_print_matching_line_nocolor(a->buf_list, 
label,
+                                               ((verbose > 1) ? lineno : -1));
+                       else if (a->do_regex)
+                               qgrep_print_matching_line_regcolor(a->buf_list, 
label,
+                                               ((verbose > 1) ? lineno : -1), 
&a->preg);
+                       else
+                               qgrep_print_matching_line_strcolor(a->buf_list, 
label,
+                                               ((verbose > 1) ? lineno : -1), 
a->strfunc,
+                                               a->query);
+               } else {
+                       /* in verbose do_list mode, list the file once
+                        * per match */
+                       printf("%s", label);
+                       if (verbose > 1)
+                               printf(":%d", lineno);
+                       putchar('\n');
+               }
+               /* init count down of trailing context lines */
+               remaining_after_context = a->num_lines_after;
+               continue;
+
+print_after_context:
+               /* print some trailing context lines when needed */
+               if (!remaining_after_context) {
+                       if (!status)
+                               /* we're getting closer to the need of a
+                                * separator between current match block and
+                                * the next one */
+                               ++need_separator;
+               } else {
+                       qgrep_print_context_line(a->buf_list, label,
+                                       ((verbose > 1) ? lineno : -1));
+                       --remaining_after_context;
+               }
+       }
+       fclose(newfp);
+       if (per_file_output) {
+               /* matches were already displayed, line per line */
+               if (a->do_count && count) {
+                       if (label != NULL)
+                               /* -c without -v/-N/-H only outputs
+                                * the matches count of the file */
+                               printf("%s:", label);
+                       printf("%d\n", count);
+               } else if ((count && !a->invert_list) ||
+                               (!count && a->invert_list))
+               {
+                       printf("%s\n", label);
+               }
+               /* do_list == 1, or we wouldn't be here */
+       }
+
+       return status;
+}
+
+static int
+qgrep_cache_cb(cache_pkg_ctx *pkg_ctx, void *priv)
+{
+       struct qgrep_grepargs *data = (struct qgrep_grepargs *)priv;
+       char buf[_Q_PATH_MAX];
+       char name[_Q_PATH_MAX];
+       char *label;
+       depend_atom *patom = NULL;
+       cache_ctx *cctx;
+       int ret;
+       int pfd;
+
+       snprintf(buf, sizeof(buf), "%s/%s",
+                       pkg_ctx->cat_ctx->name, pkg_ctx->name);
+       patom = atom_explode(buf);
+       if (patom == NULL)
+               return EXIT_FAILURE;
+
+       if (data->include_atoms != NULL) {
+               depend_atom **d;
+               for (d = data->include_atoms; *d != NULL; d++) {
+                       if (atom_compare(patom, *d) == EQUAL)
+                               break;
+               }
+               if (*d == NULL) {
+                       atom_implode(patom);
+                       return EXIT_FAILURE;
+               }
+       }
+
+       /* need to construct path in portdir to ebuild, pass it to grep */
+       cctx = (cache_ctx *)(pkg_ctx->cat_ctx->ctx);
+       if (cctx->cachetype == CACHE_EBUILD) {
+               pfd = cctx->dir_ctx->vdb_fd;
+       } else {
+               pfd = openat(cctx->dir_ctx->vdb_fd, "../..", 
O_RDONLY|O_CLOEXEC);
+       }
+
+       /* cat/pkg/pkg-ver.ebuild */
+       snprintf(buf, sizeof(buf), "%s/%s/%s.ebuild",
+                       patom->CATEGORY, patom->PN, patom->P);
+
+       label = NULL;
+       if (data->show_name) {
+               snprintf(name, sizeof(name), "%s/%s", patom->CATEGORY, 
patom->P);
+               label = name;
+       } else if (data->show_filename) {
+               label = buf;
+       }
+
+       ret = qgrep_grepat(pfd, buf, label, data);
+
+       atom_implode(patom);
+
+       return ret;
+}
+
+static int
+qgrep_vdb_cb(q_vdb_pkg_ctx *pkg_ctx, void *priv)
 {
-       struct dirent *pkg_dirent = NULL;
-       if (*cat_dirent_pt == NULL || *cat_dir_pt == NULL)
-               goto get_next_category;
-get_next_ebuild_from_category:
-       if ((pkg_dirent = readdir(*cat_dir_pt)) == NULL)
-               goto get_next_category;
-       if (pkg_dirent->d_name[0] == '.')
-               goto get_next_ebuild_from_category;
-       snprintf(ebuild_path, ebuild_path_len, "%s/%s/%s.ebuild",
-                       (*cat_dirent_pt)->d_name, pkg_dirent->d_name, 
pkg_dirent->d_name);
-       return ebuild_path;
-get_next_category:
-       if (*cat_dir_pt != NULL)
-               closedir(*cat_dir_pt);
-       *cat_dirent_pt = q_vdb_get_next_dir(vdb_dir);
-       if (*cat_dirent_pt == NULL)
-               return NULL;
-       if ((*cat_dir_pt = opendir((*cat_dirent_pt)->d_name)) == NULL)
-               goto get_next_category;
-       goto get_next_ebuild_from_category;
+       struct qgrep_grepargs *data = (struct qgrep_grepargs *)priv;
+       char buf[_Q_PATH_MAX];
+       char name[_Q_PATH_MAX];
+       char *label;
+       depend_atom *patom = NULL;
+       int ret;
+       int pfd;
+
+       snprintf(buf, sizeof(buf), "%s/%s",
+                       pkg_ctx->cat_ctx->name, pkg_ctx->name);
+       patom = atom_explode(buf);
+       if (patom == NULL)
+               return EXIT_FAILURE;
+
+       if (data->include_atoms != NULL) {
+               depend_atom **d;
+               for (d = data->include_atoms; *d != NULL; d++) {
+                       if (atom_compare(patom, *d) == EQUAL)
+                               break;
+               }
+               if (*d == NULL) {
+                       atom_implode(patom);
+                       return EXIT_FAILURE;
+               }
+       }
+
+       /* get path to portdir */
+       pfd = openat(pkg_ctx->cat_ctx->ctx->portroot_fd,
+                       data->portdir, O_RDONLY|O_CLOEXEC);
+
+       /* cat/pkg/pkg-ver.ebuild */
+       snprintf(buf, sizeof(buf), "%s/%s/%s.ebuild",
+                       patom->CATEGORY, patom->PN, patom->P);
+
+       label = NULL;
+       if (data->show_name) {
+               snprintf(name, sizeof(name), "%s/%s", patom->CATEGORY, 
patom->P);
+               label = name;
+       } else if (data->show_filename) {
+               label = buf;
+       }
+
+       ret = qgrep_grepat(pfd, buf, label, data);
+
+       atom_implode(patom);
+
+       return ret;
 }
 
 int qgrep_main(int argc, char **argv)
 {
        int i;
-       int count = 0;
        char *p;
-       char do_count, do_regex, do_eclass, do_installed, do_list;
-       char show_filename, skip_comments, invert_list, show_name;
-       char per_file_output;
-       FILE *fp = NULL;
+       bool do_eclass;
+       bool do_installed;
        DIR *eclass_dir = NULL;
-       DIR *vdb_dir = NULL;
-       DIR *cat_dir = NULL;
        struct dirent *dentry = NULL;
-       char ebuild[_Q_PATH_MAX * 4];
-       char name[_Q_PATH_MAX * 2];
-       char *label;
        int reflags = 0;
-       char invert_match = 0;
-       regex_t preg, skip_preg;
-       char *skip_pattern = NULL;
-       depend_atom** include_atoms = NULL;
        unsigned long int context_optarg;
-       char num_lines_before = 0;
-       char num_lines_after = 0;
-       qgrep_buf_t *buf_list;
-       int need_separator = 0;
        char status = 1;
+       size_t n;
+       char *overlay;
 
-       QGREP_STR_FUNC strfunc = strstr;
-
-       do_count = do_regex = do_eclass = do_installed = do_list = 0;
-       show_filename = skip_comments = invert_list = show_name = 0;
+       struct qgrep_grepargs args = {
+               .do_count = 0,
+               .do_regex = 0,
+               .do_list = 0,
+               .show_filename = 0,
+               .show_name = 0,
+               .skip_comments = 0,
+               .invert_list = 0,
+               .invert_match = 0,
+               .skip_pattern = NULL,
+               .num_lines_before = 0,
+               .num_lines_after = 0,
+               .buf_list = NULL,
+               .query = NULL,
+               .strfunc = strstr,
+               .include_atoms = NULL,
+               .portdir = NULL,
+       };
+
+       do_eclass = do_installed = 0;
 
        while ((i = GETOPT_LONG(QGREP, qgrep, "")) != -1) {
                switch (i) {
-               case 'I': invert_match = 1; break;
+               case 'I': args.invert_match = 1; break;
                case 'i':
-                       strfunc = strcasestr;
+                       args.strfunc = strcasestr;
                        reflags |= REG_ICASE;
                        break;
-               case 'c': do_count = 1; break;
-               case 'l': do_list = 1; break;
-               case 'L': do_list = invert_list = 1; break;
-               case 'e': do_regex = 1; break;
+               case 'c': args.do_count = 1; break;
+               case 'l': args.do_list = 1; break;
+               case 'L': args.do_list = args.invert_list = 1; break;
+               case 'e': args.do_regex = 1; break;
                case 'x':
-                       do_regex = 1;
+                       args.do_regex = 1;
                        reflags |= REG_EXTENDED;
                        break;
                case 'J': do_installed = 1; break;
                case 'E': do_eclass = 1; break;
-               case 'H': show_filename = 1; break;
-               case 'N': show_name = 1; break;
-               case 's': skip_comments = 1; break;
-               case 'S': skip_pattern = optarg; break;
+               case 'H': args.show_filename = 1; break;
+               case 'N': args.show_name = 1; break;
+               case 's': args.skip_comments = 1; break;
+               case 'S': args.skip_pattern = optarg; break;
                case 'B':
                case 'A':
                        errno = 0;
@@ -317,9 +553,9 @@ int qgrep_main(int argc, char **argv)
                        if (context_optarg > 254)
                                err("%s: silly value!", optarg);
                        if (i == 'B')
-                               num_lines_before = context_optarg;
+                               args.num_lines_before = context_optarg;
                        else
-                               num_lines_after = context_optarg;
+                               args.num_lines_after = context_optarg;
                        break;
                COMMON_GETOPTS_CASES(qgrep)
                }
@@ -327,312 +563,140 @@ int qgrep_main(int argc, char **argv)
        if (argc == optind)
                qgrep_usage(EXIT_FAILURE);
 
-       if (do_list && do_count) {
+       if (args.do_list && args.do_count) {
                warn("%s and --count are incompatible options. The former 
wins.",
-                               (invert_list ? "--invert-list" : "--list"));
-               do_count = 0;
+                               (args.invert_list ? "--invert-list" : 
"--list"));
+               args.do_count = false;
        }
 
-       if (show_name && show_filename) {
+       if (args.show_name && args.show_filename) {
                warn("--with-name and --with-filename are incompatible options. 
"
                                "The former wins.");
-               show_filename = 0;
+               args.show_filename = false;
        }
 
-       if (do_list && num_lines_before) {
+       if (args.do_list && args.num_lines_before) {
                warn("%s and --before are incompatible options. The former 
wins.",
-                               (invert_list ? "--invert-list" : "--list"));
-               num_lines_before = 0;
+                               (args.invert_list ? "--invert-list" : 
"--list"));
+               args.num_lines_before = 0;
        }
 
-       if (do_list && num_lines_after) {
+       if (args.do_list && args.num_lines_after) {
                warn("%s and --after are incompatible options. The former 
wins.",
-                               (invert_list ? "--invert-list" : "--list"));
-               num_lines_after = 0;
+                               (args.invert_list ? "--invert-list" : 
"--list"));
+               args.num_lines_after = 0;
        }
 
-       if (do_count && num_lines_before) {
+       if (args.do_count && args.num_lines_before) {
                warn("--count and --before are incompatible options. The former 
wins.");
-               num_lines_before = 0;
+               args.num_lines_before = 0;
        }
 
-       if (do_count && num_lines_after) {
+       if (args.do_count && args.num_lines_after) {
                warn("--count and --after are incompatible options. The former 
wins.");
-               num_lines_after = 0;
+               args.num_lines_after = 0;
        }
 
        if (do_installed && do_eclass) {
                warn("--installed and --eclass are incompatible options. "
                                "The former wins.");
-               do_eclass = 0;
+               do_eclass = false;
        }
 
-       /* do we report results once per file or per line ? */
-       per_file_output = do_count || (do_list && (!verbose || invert_list));
-       /* label for prefixing matching lines or listing matching files */
-       label = (show_name ? name :
-                       ((verbose || show_filename || do_list) ? ebuild : 
NULL));
-
        if (argc > (optind + 1)) {
-               include_atoms = xcalloc(sizeof(depend_atom*), (argc - optind - 
1));
-               for (i = (optind + 1); i < argc; i++)
-                       if ((include_atoms[i - optind - 1] = 
atom_explode(argv[i])) == NULL)
+               depend_atom **d = args.include_atoms =
+                       xcalloc(sizeof(depend_atom *), (argc - optind - 1) + 1);
+               for (i = (optind + 1); i < argc; i++) {
+                       *d = atom_explode(argv[i]);
+                       if (*d == NULL) {
                                warn("%s: invalid atom, will be ignored", 
argv[i]);
+                       } else {
+                               d++;
+                       }
+               }
+               *d = NULL;
        }
 
+       /* make it easier to see what needs to be printed */
+       if (!args.show_name && (verbose || args.do_list))
+               args.show_filename = true;
+
        /* pre-compile regexps once for all */
-       if (do_regex) {
-               if (invert_match || *RED == '\0')
+       if (args.do_regex) {
+               if (args.invert_match || *RED == '\0')
                        reflags |= REG_NOSUB;
-               xregcomp(&preg, argv[optind], reflags);
+               xregcomp(&args.preg, argv[optind], reflags);
                reflags |= REG_NOSUB;
-               if (skip_pattern)
-                       xregcomp(&skip_preg, skip_pattern, reflags);
+               if (args.skip_pattern)
+                       xregcomp(&args.skip_preg, args.skip_pattern, reflags);
        }
+       args.query = argv[optind];
 
        /* allocate a circular buffers list for --before */
-       buf_list = qgrep_buf_list_alloc(num_lines_before + 1);
+       args.buf_list = qgrep_buf_list_alloc(args.num_lines_before + 1);
 
-       size_t n;
-       char *overlay;
        array_for_each(overlays, n, overlay) {
+               args.portdir = overlay;
+               if (do_eclass) {
+                       char buf[_Q_PATH_MAX];
+                       char name[_Q_PATH_MAX];
+                       char *label;
+                       int efd;
 
-               /* go look either in ebuilds or eclasses or VDB */
-               /* FIXME: use libq/vdb and libq/cache here */
-               if (!do_eclass && !do_installed) {
-                       /* TODO: use libq/cache here */ continue;
-               } else if (do_eclass) {
-                       xchdir(overlay);
-                       if ((eclass_dir = opendir("eclass")) == NULL) {
+                       snprintf(buf, sizeof(buf), "%s/%s/eclass", portroot, 
overlay);
+                       efd = open(buf, O_RDONLY|O_CLOEXEC);
+                       if (efd == -1 || (eclass_dir = fdopendir(efd)) == NULL) 
{
                                if (errno != ENOENT)
                                        warnp("opendir(\"%s/eclass\") failed", 
overlay);
                                continue;
                        }
-               } else { /* if (do_install) */
-                       /* TODO: use libq/vdb here */
-                       char buf[_Q_PATH_MAX];
-                       snprintf(buf, sizeof(buf), "%s/%s", portroot, portvdb);
-                       xchdir(buf);
-                       if ((vdb_dir = opendir(".")) == NULL)
-                               errp("could not opendir(%s/%s) for ROOT/VDB",
-                                               portroot, portvdb);
-               }
-
-               /* iteration is either over ebuilds or eclasses */
-               while (do_eclass
-                               ? ((dentry = readdir(eclass_dir))
-                                       && snprintf(ebuild, sizeof(ebuild),
-                                               "eclass/%s", dentry->d_name))
-                               : (do_installed
-                                       ? (get_next_installed_ebuild(ebuild, 
sizeof(ebuild),
-                                                       vdb_dir, &dentry, 
&cat_dir) != NULL)
-                                       : (fgets(ebuild, sizeof(ebuild), fp) != 
NULL)))
-               {
-                       FILE *newfp;
-
-                       /* filter badly named files, prepare eclass or package 
name, etc. */
-                       if (do_eclass) {
-                               if ((p = strrchr(ebuild, '.')) == NULL)
-                                       continue;
-                               if (strcmp(p, ".eclass"))
+                       while ((dentry = readdir(eclass_dir)) != NULL) {
+                               if (strstr(dentry->d_name, ".eclass") == NULL)
                                        continue;
-                               if (show_name || (include_atoms != NULL)) {
-                                       /* cut ".eclass" */
-                                       *p = '\0';
-                                       /* and skip "eclass/" */
-                                       snprintf(name, sizeof(name), "%s", 
ebuild + 7);
-                                       /* restore the filepath */
-                                       *p = '.';
-                               }
-                       } else {
-                               if ((p = strchr(ebuild, '\n')) != NULL)
-                                       *p = '\0';
-                               if (show_name || (include_atoms != NULL)) {
-                                       size_t l;
-                                       /* cut ".ebuild" */
-                                       if (p == NULL)
-                                               p = ebuild + strlen(ebuild);
-                                       *(p-7) = '\0';
-                                       /* cut "/foo/" from "cat/foo/foo-x.y" */
-                                       if ((p = strchr(ebuild, '/')) == NULL)
-                                               continue;
-                                       *(p++) = '\0';
-                                       /* find head of the ebuild basename */
-                                       if ((p = strchr(p, '/')) == NULL)
-                                               continue;
-                                       /* find start of the pkg name, break up 
in two to
-                                        * avoid warning about possible 
truncation (very
-                                        * unlikely) */
-                                       l = snprintf(name, sizeof(name), "%s", 
ebuild);
-                                       snprintf(name + l, sizeof(name) - l, 
"%s", p);
-                                       /* restore the filepath */
-                                       *p = '/';
-                                       *(p + strlen(p)) = '.';
-                                       ebuild[strlen(ebuild)] = '/';
-                               }
-                       }
-
-                       /* filter the files we grep when there are extra args */
-                       if (include_atoms != NULL)
-                               if (!qgrep_name_match(name, (argc - optind - 
1), include_atoms))
-                                       continue;
-
-                       if ((newfp = fopen(ebuild, "r")) != NULL) {
-                               int lineno = 0;
-                               char remaining_after_context = 0;
-                               count = 0;
-                               /* if there have been some matches already, 
then a
-                                * separator will be needed */
-                               need_separator =
-                                       !status && (num_lines_before || 
num_lines_after);
-                               /* whatever is in the circular buffers list is 
no more a
-                                * valid context */
-                               qgrep_buf_list_invalidate(buf_list);
-
-                               /* reading a new line always happen in the next 
buffer
-                                * of the list */
-                               while ((buf_list = buf_list->next) &&
-                                               fgets(buf_list->buf, 
sizeof(buf_list->buf), newfp))
-                               {
-                                       lineno++;
-                                       buf_list->valid = 1;
-
-                                       /* cleanup EOL */
-                                       if ((p = strrchr(buf_list->buf, '\n')) 
!= NULL)
-                                               *p = 0;
-                                       if ((p = strrchr(buf_list->buf, '\r')) 
!= NULL)
-                                               *p = 0;
-
-                                       if (skip_comments) {
-                                               /* reject comments line ("^[ 
\t]*#") */
-                                               p = buf_list->buf;
-                                               while (*p == ' ' || *p == '\t') 
p++;
-                                               if (*p == '#')
-                                                       goto 
print_after_context;
+                               /* filter the files we grep when there are 
extra args */
+                               if (args.include_atoms != NULL) {
+                                       depend_atom **d;
+                                       for (d = args.include_atoms; *d != 
NULL; d++) {
+                                               if ((*d)->PN != NULL && 
strncmp(dentry->d_name,
+                                                                       
(*d)->PN, strlen((*d)->PN)) == 0)
+                                                       break;
                                        }
-
-                                       if (skip_pattern) {
-                                               /* reject some other lines 
which match an
-                                                * optional pattern */
-                                               if (!do_regex) {
-                                                       if 
(strfunc(buf_list->buf, skip_pattern) != NULL)
-                                                               goto 
print_after_context;
-                                               } else {
-                                                       if (regexec(&skip_preg, 
buf_list->buf,
-                                                                               
0, NULL, 0) == 0)
-                                                               goto 
print_after_context;
-                                               }
-                                       }
-
-                                       /* four ways to match a line 
(with/without inversion
-                                        * and regexp) */
-                                       if (!invert_match) {
-                                               if (do_regex == 0) {
-                                                       if 
(strfunc(buf_list->buf, argv[optind]) == NULL)
-                                                               goto 
print_after_context;
-                                               } else {
-                                                       if (regexec(&preg, 
buf_list->buf, 0, NULL, 0) != 0)
-                                                               goto 
print_after_context;
-                                               }
-                                       } else {
-                                               if (do_regex == 0) {
-                                                       if 
(strfunc(buf_list->buf, argv[optind]) != NULL)
-                                                               goto 
print_after_context;
-                                               } else {
-                                                       if (regexec(&preg, 
buf_list->buf, 0, NULL, 0) == 0)
-                                                               goto 
print_after_context;
-                                               }
-                                       }
-
-                                       count++;
-                                       status = 0; /* got a match, exit status 
should be 0 */
-                                       if (per_file_output)
+                                       if (*d == NULL)
                                                continue;
-                                               /* matching files are listed 
out of this loop */
-
-                                       if ((need_separator > 0)
-                                                       && (num_lines_before || 
num_lines_after))
-                                               printf("--\n");
-                                       /* "need_separator" is not a flag, but 
a counter, so that
-                                        * adjacent contextes are not separated 
*/
-                                       need_separator = 0 - num_lines_before;
-                                       if (!do_list) {
-                                               /* print the leading context */
-                                               
qgrep_print_before_context(buf_list,
-                                                               
num_lines_before, label,
-                                                               ((verbose > 1) 
? lineno : -1));
-                                               /* print matching line */
-                                               if (invert_match || *RED == 
'\0')
-                                                       
qgrep_print_matching_line_nocolor(buf_list, label,
-                                                               ((verbose > 1) 
? lineno : -1));
-                                               else if (do_regex)
-                                                       
qgrep_print_matching_line_regcolor(buf_list, label,
-                                                               ((verbose > 1) 
? lineno : -1), &preg);
-                                               else
-                                                       
qgrep_print_matching_line_strcolor(buf_list, label,
-                                                               ((verbose > 1) 
? lineno : -1), strfunc,
-                                                               argv[optind]);
-                                       } else {
-                                               /* in verbose do_list mode, 
list the file once
-                                                * per match */
-                                               printf("%s", label);
-                                               if (verbose > 1)
-                                                       printf(":%d", lineno);
-                                               putchar('\n');
-                                       }
-                                       /* init count down of trailing context 
lines */
-                                       remaining_after_context = 
num_lines_after;
-                                       continue;
+                               }
 
- print_after_context:
-                                       /* print some trailing context lines 
when needed */
-                                       if (!remaining_after_context) {
-                                               if (!status)
-                                                       /* we're getting closer 
to the need of a
-                                                        * separator between 
current match block and
-                                                        * the next one */
-                                                       ++need_separator;
-                                       } else {
-                                               
qgrep_print_context_line(buf_list, label,
-                                                               ((verbose > 1) 
? lineno : -1));
-                                               --remaining_after_context;
-                                       }
+                               label = NULL;
+                               if (args.show_name) {
+                                       snprintf(name, sizeof(name), "%.*s",
+                                                       
(int)(strlen(dentry->d_name) - 7), dentry->d_name);
+                                       label = name;
+                               } else if (args.show_filename) {
+                                       snprintf(name, sizeof(name), 
"eclass/%s", dentry->d_name);
+                                       label = name;
                                }
-                               fclose(newfp);
-                               if (!per_file_output)
-                                       continue;
-                                       /* matches were already displayed, line 
per line */
-                               if (do_count && count) {
-                                       if (label != NULL)
-                                               /* -c without -v/-N/-H only 
outputs
-                                                * the matches count of the 
file */
-                                               printf("%s:", label);
-                                       printf("%d\n", count);
-                               } else if ((count && !invert_list) || (!count 
&& invert_list))
-                                       printf("%s\n", label);
-                                       /* do_list == 1, or we wouldn't be here 
*/
+                               status = qgrep_grepat(efd, dentry->d_name, 
label, &args);
                        }
-               }
-               if (do_eclass)
                        closedir(eclass_dir);
-               else if (!do_installed)
-                       fclose(fp);
-
-               if (do_installed)
-                       break;
+               } else if (do_installed) {
+                       status = q_vdb_foreach_pkg(portroot, portvdb,
+                                       qgrep_vdb_cb, &args, NULL);
+               } else { /* do_ebuild */
+                       status = cache_foreach_pkg(portroot, overlay,
+                                       qgrep_cache_cb, &args, NULL);
+               }
        }
 
-       if (do_regex)
-               regfree(&preg);
-       if (do_regex && skip_pattern)
-               regfree(&skip_preg);
-       if (include_atoms != NULL) {
+       if (args.do_regex)
+               regfree(&args.preg);
+       if (args.do_regex && args.skip_pattern)
+               regfree(&args.skip_preg);
+       if (args.include_atoms != NULL) {
                for (i = 0; i < (argc - optind - 1); i++)
-                       if (include_atoms[i] != NULL)
-                               atom_implode(include_atoms[i]);
-               free(include_atoms);
+                       if (args.include_atoms[i] != NULL)
+                               atom_implode(args.include_atoms[i]);
+               free(args.include_atoms);
        }
-       qgrep_buf_list_free(buf_list);
+       qgrep_buf_list_free(args.buf_list);
 
        return status;
 }

Reply via email to