From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by finch.gentoo.org (Postfix) with ESMTPS id 4B9B5138334 for ; Sun, 28 Apr 2019 07:58:34 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id 3C91AE0897; Sun, 28 Apr 2019 07:58:33 +0000 (UTC) Received: from smtp.gentoo.org (woodpecker.gentoo.org [IPv6:2001:470:ea4a:1:5054:ff:fec7:86e4]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id 0F4D7E0897 for ; Sun, 28 Apr 2019 07:58:32 +0000 (UTC) Received: from oystercatcher.gentoo.org (unknown [IPv6:2a01:4f8:202:4333:225:90ff:fed9:fc84]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.gentoo.org (Postfix) with ESMTPS id 3C9C1342AE0 for ; Sun, 28 Apr 2019 07:58:31 +0000 (UTC) Received: from localhost.localdomain (localhost [IPv6:::1]) by oystercatcher.gentoo.org (Postfix) with ESMTP id 7D5D155B for ; Sun, 28 Apr 2019 07:58:29 +0000 (UTC) From: "Fabian Groffen" To: gentoo-commits@lists.gentoo.org Content-Transfer-Encoding: 8bit Content-type: text/plain; charset=UTF-8 Reply-To: gentoo-dev@lists.gentoo.org, "Fabian Groffen" Message-ID: <1556438225.c5aba3a0bd055688120dbabb9c3826ed46ffc795.grobian@gentoo> Subject: [gentoo-commits] proj/portage-utils:master commit in: man/, /, man/include/ X-VCS-Repository: proj/portage-utils X-VCS-Files: applets.h man/include/qgrep.desc man/include/qgrep.optdesc.yaml man/q.1 man/qgrep.1 qgrep.c X-VCS-Directories: man/ / man/include/ X-VCS-Committer: grobian X-VCS-Committer-Name: Fabian Groffen X-VCS-Revision: c5aba3a0bd055688120dbabb9c3826ed46ffc795 X-VCS-Branch: master Date: Sun, 28 Apr 2019 07:58:29 +0000 (UTC) Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-commits@lists.gentoo.org X-Auto-Response-Suppress: DR, RN, NRN, OOF, AutoReply X-Archives-Salt: 084f9cda-6655-4414-abdd-356444acfd84 X-Archives-Hash: 54b0bc1ffe0d5abc6fbb20f15c43847c commit: c5aba3a0bd055688120dbabb9c3826ed46ffc795 Author: Fabian Groffen gentoo org> AuthorDate: Sun Apr 28 07:57:05 2019 +0000 Commit: Fabian Groffen gentoo 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 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, " ", "check GLSAs against system"}, */ - {"qgrep", qgrep_main, "", "grep in ebuilds"}, + {"qgrep", qgrep_main, " [pkg ...]", "grep in ebuilds"}, {"qlist", qlist_main, "", "list files owned by pkgname"}, {"qlop", qlop_main, "", "emerge log analyzer"}, {"qmerge", qmerge_main, "", "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 : verify integrity of installed packages qdepends : show dependency info qfile : list all pkgs owning files - qgrep : grep in ebuilds + qgrep [pkg ...]: grep in ebuilds qlist : list files owned by pkgname qlop : emerge log analyzer qmerge : 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] \fR +\fI[opts] [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 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; }