Gentoo Archives: gentoo-commits

From: Fabian Groffen <grobian@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] proj/portage-utils:master commit in: man/, /, man/include/
Date: Sun, 28 Apr 2019 07:58:34
Message-Id: 1556438225.c5aba3a0bd055688120dbabb9c3826ed46ffc795.grobian@gentoo
1 commit: c5aba3a0bd055688120dbabb9c3826ed46ffc795
2 Author: Fabian Groffen <grobian <AT> gentoo <DOT> org>
3 AuthorDate: Sun Apr 28 07:57:05 2019 +0000
4 Commit: Fabian Groffen <grobian <AT> gentoo <DOT> org>
5 CommitDate: Sun Apr 28 07:57:05 2019 +0000
6 URL: https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=c5aba3a0
7
8 qgrep: rewrite using libq/vdb and libq/cache
9
10 (re)use the traversion logic from libq instead of re-implementing this.
11 Additional benefits are less code and usage of metadata when available.
12
13 Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org>
14
15 applets.h | 2 +-
16 man/include/qgrep.desc | 9 +-
17 man/include/qgrep.optdesc.yaml | 5 +-
18 man/q.1 | 2 +-
19 man/qgrep.1 | 16 +-
20 qgrep.c | 756 ++++++++++++++++++++++-------------------
21 6 files changed, 429 insertions(+), 361 deletions(-)
22
23 diff --git a/applets.h b/applets.h
24 index 5889de6..4dd5bc2 100644
25 --- a/applets.h
26 +++ b/applets.h
27 @@ -73,7 +73,7 @@ static const struct applet_t {
28 /*
29 {"qglsa", qglsa_main, "<action> <list>", "check GLSAs against system"},
30 */
31 - {"qgrep", qgrep_main, "<misc args>", "grep in ebuilds"},
32 + {"qgrep", qgrep_main, "<expr> [pkg ...]", "grep in ebuilds"},
33 {"qlist", qlist_main, "<pkgname>", "list files owned by pkgname"},
34 {"qlop", qlop_main, "<pkgname>", "emerge log analyzer"},
35 {"qmerge", qmerge_main, "<pkgnames>", "fetch and merge binary package"},
36
37 diff --git a/man/include/qgrep.desc b/man/include/qgrep.desc
38 index c95d35a..98bcc09 100644
39 --- a/man/include/qgrep.desc
40 +++ b/man/include/qgrep.desc
41 @@ -1,3 +1,6 @@
42 -\fIqgrep\fR searches for a given pattern in all ebuilds of the current
43 -portage tree. Optionally the search is in all eclasses, or just in the
44 -ebuilds that are currently installed.
45 +\fIqgrep\fR searches for a given expression in all ebuilds of the
46 +current portage tree and defined additional overlays. Optionally the
47 +search is in all eclasses, or just in the ebuilds that are currently
48 +installed. To narrow the search, multiple targets can be given using
49 +atom syntax. In particular, the trailing slash (/) syntax can be used
50 +to match an entire category. See also \fIqatom\fR(1).
51
52 diff --git a/man/include/qgrep.optdesc.yaml b/man/include/qgrep.optdesc.yaml
53 index 87b174e..ad3874d 100644
54 --- a/man/include/qgrep.optdesc.yaml
55 +++ b/man/include/qgrep.optdesc.yaml
56 @@ -1,5 +1,4 @@
57 verbose: |
58 - Print multiple matches per files. When this option is given
59 - multiple times, also linenumber are printed for matches next to file
60 - names.
61 + Prefix each matching line with filename (like \fB-H\fR). When this
62 + option is given multiple times, also linenumbers are printed.
63 quiet: Ignored for compatibility with other qapplets.
64
65 diff --git a/man/q.1 b/man/q.1
66 index b3e985b..afba24a 100644
67 --- a/man/q.1
68 +++ b/man/q.1
69 @@ -41,7 +41,7 @@ Print version and exit.
70 qcheck <pkgname> : verify integrity of installed packages
71 qdepends <pkgname> : show dependency info
72 qfile <filename> : list all pkgs owning files
73 - qgrep <misc args> : grep in ebuilds
74 + qgrep <expr> [pkg ...]: grep in ebuilds
75 qlist <pkgname> : list files owned by pkgname
76 qlop <pkgname> : emerge log analyzer
77 qmerge <pkgnames> : fetch and merge binary package
78
79 diff --git a/man/qgrep.1 b/man/qgrep.1
80 index 662122d..048e28e 100644
81 --- a/man/qgrep.1
82 +++ b/man/qgrep.1
83 @@ -4,11 +4,14 @@
84 qgrep \- grep in ebuilds
85 .SH SYNOPSIS
86 .B qgrep
87 -\fI[opts] <misc args>\fR
88 +\fI[opts] <expr> [pkg ...]\fR
89 .SH DESCRIPTION
90 -\fIqgrep\fR searches for a given pattern in all ebuilds of the current
91 -portage tree. Optionally the search is in all eclasses, or just in the
92 -ebuilds that are currently installed.
93 +\fIqgrep\fR searches for a given expression in all ebuilds of the
94 +current portage tree and defined additional overlays. Optionally the
95 +search is in all eclasses, or just in the ebuilds that are currently
96 +installed. To narrow the search, multiple targets can be given using
97 +atom syntax. In particular, the trailing slash (/) syntax can be used
98 +to match an entire category. See also \fIqatom\fR(1).
99 .SH OPTIONS
100 .TP
101 \fB\-I\fR, \fB\-\-invert\-match\fR
102 @@ -60,9 +63,8 @@ Print <arg> lines of trailing context.
103 Set the ROOT env var.
104 .TP
105 \fB\-v\fR, \fB\-\-verbose\fR
106 -Print multiple matches per files. When this option is given
107 -multiple times, also linenumber are printed for matches next to file
108 -names.
109 +Prefix each matching line with filename (like \fB-H\fR). When this
110 +option is given multiple times, also linenumbers are printed.
111 .TP
112 \fB\-q\fR, \fB\-\-quiet\fR
113 Ignored for compatibility with other qapplets.
114
115 diff --git a/qgrep.c b/qgrep.c
116 index 16bb4c1..3950c22 100644
117 --- a/qgrep.c
118 +++ b/qgrep.c
119 @@ -64,35 +64,6 @@ static const char * const qgrep_opts_help[] = {
120 };
121 #define qgrep_usage(ret) usage(ret, QGREP_FLAGS, qgrep_long_opts, qgrep_opts_help, NULL, lookup_applet_idx("qgrep"))
122
123 -static char
124 -qgrep_name_match(const char* name, const int argc, depend_atom** argv)
125 -{
126 - depend_atom* atom;
127 - int i;
128 -
129 - if ((atom = atom_explode(name)) == NULL)
130 - return 0;
131 -
132 - for (i = 0; i < argc; i++) {
133 - if (argv[i] == NULL)
134 - continue;
135 - if (atom->CATEGORY && argv[i]->CATEGORY && *(argv[i]->CATEGORY)
136 - && strcmp(atom->CATEGORY, argv[i]->CATEGORY))
137 - continue;
138 - if (atom->PN && argv[i]->PN && *(argv[i]->PN)
139 - && strcmp(atom->PN, argv[i]->PN))
140 - continue;
141 - if (atom->PVR && argv[i]->PVR && *(argv[i]->PVR)
142 - && strcmp(atom->PVR, argv[i]->PVR))
143 - continue;
144 - atom_implode(atom);
145 - return 1;
146 - }
147 -
148 - atom_implode(atom);
149 - return 0;
150 -}
151 -
152 /* Circular list of line buffers for --before */
153 typedef struct qgrep_buf {
154 char valid;
155 @@ -221,91 +192,356 @@ qgrep_print_before_context(qgrep_buf_t *current, const char num_lines_before,
156 }
157 }
158
159 -/* Yield the path of one of the installed ebuilds (from VDB). */
160 -static char *
161 -get_next_installed_ebuild(
162 - char *ebuild_path,
163 - size_t ebuild_path_len,
164 - DIR *vdb_dir,
165 - struct dirent **cat_dirent_pt,
166 - DIR **cat_dir_pt)
167 +struct qgrep_grepargs {
168 + bool do_count:1;
169 + bool do_regex:1;
170 + bool do_list:1;
171 + bool show_filename:1;
172 + bool show_name:1;
173 + bool skip_comments:1;
174 + bool invert_list:1;
175 + bool invert_match:1;
176 + char *skip_pattern;
177 + char num_lines_before;
178 + char num_lines_after;
179 + qgrep_buf_t *buf_list;
180 + regex_t skip_preg;
181 + regex_t preg;
182 + const char *query;
183 + QGREP_STR_FUNC strfunc;
184 + depend_atom **include_atoms;
185 + const char *portdir;
186 +};
187 +
188 +static int
189 +qgrep_grepat(int fd, const char *file, const char *label,
190 + struct qgrep_grepargs *a)
191 +{
192 + FILE *newfp;
193 + int need_separator = 0;
194 + int count = 0;
195 + int lineno = 0;
196 + char remaining_after_context = 0;
197 + char status = 1;
198 + char *p;
199 + bool per_file_output;
200 +
201 + /* do we report results once per file or per line ? */
202 + per_file_output =
203 + a->do_count || (a->do_list && (!verbose || a->invert_list));
204 +
205 + if (fd >= 0) {
206 + int sfd = openat(fd, file, O_RDONLY|O_CLOEXEC);
207 + newfp = sfd >= 0 ? fdopen(sfd, "r") : NULL;
208 + } else {
209 + newfp = fopen(file, "r");
210 + }
211 + if (newfp == NULL)
212 + return status;
213 +
214 + count = 0;
215 + /* if there have been some matches already, then a
216 + * separator will be needed */
217 + need_separator =
218 + !status && (a->num_lines_before || a->num_lines_after);
219 + /* whatever is in the circular buffers list is no more a
220 + * valid context */
221 + qgrep_buf_list_invalidate(a->buf_list);
222 +
223 + /* reading a new line always happen in the next buffer
224 + * of the list */
225 + while ((a->buf_list = a->buf_list->next) &&
226 + fgets(a->buf_list->buf, sizeof(a->buf_list->buf), newfp))
227 + {
228 + lineno++;
229 + a->buf_list->valid = 1;
230 +
231 + /* cleanup EOL */
232 + if ((p = strrchr(a->buf_list->buf, '\n')) != NULL)
233 + *p = 0;
234 + if ((p = strrchr(a->buf_list->buf, '\r')) != NULL)
235 + *p = 0;
236 +
237 + if (a->skip_comments) {
238 + /* reject comments line ("^[ \t]*#") */
239 + p = a->buf_list->buf;
240 + while (*p == ' ' || *p == '\t') p++;
241 + if (*p == '#')
242 + goto print_after_context;
243 + }
244 +
245 + if (a->skip_pattern) {
246 + /* reject some other lines which match an
247 + * optional pattern */
248 + if (!a->do_regex) {
249 + if (a->strfunc(a->buf_list->buf, a->skip_pattern) != NULL)
250 + goto print_after_context;
251 + } else {
252 + if (regexec(&a->skip_preg, a->buf_list->buf,
253 + 0, NULL, 0) == 0)
254 + goto print_after_context;
255 + }
256 + }
257 +
258 + /* four ways to match a line (with/without inversion
259 + * and regexp) */
260 + if (!a->invert_match) {
261 + if (a->do_regex == 0) {
262 + if (a->strfunc(a->buf_list->buf, a->query) == NULL)
263 + goto print_after_context;
264 + } else {
265 + if (regexec(&a->preg, a->buf_list->buf, 0, NULL, 0) != 0)
266 + goto print_after_context;
267 + }
268 + } else {
269 + if (a->do_regex == 0) {
270 + if (a->strfunc(a->buf_list->buf, a->query) != NULL)
271 + goto print_after_context;
272 + } else {
273 + if (regexec(&a->preg, a->buf_list->buf, 0, NULL, 0) == 0)
274 + goto print_after_context;
275 + }
276 + }
277 +
278 + count++;
279 + status = 0; /* got a match, exit status should be 0 */
280 + if (per_file_output)
281 + continue;
282 + /* matching files are listed out of this loop */
283 +
284 + if ((need_separator > 0)
285 + && (a->num_lines_before || a->num_lines_after))
286 + printf("--\n");
287 + /* "need_separator" is not a flag, but a counter, so that
288 + * adjacent contextes are not separated */
289 + need_separator = 0 - a->num_lines_before;
290 + if (!a->do_list) {
291 + /* print the leading context */
292 + qgrep_print_before_context(a->buf_list,
293 + a->num_lines_before, label,
294 + ((verbose > 1) ? lineno : -1));
295 + /* print matching line */
296 + if (a->invert_match || *RED == '\0')
297 + qgrep_print_matching_line_nocolor(a->buf_list, label,
298 + ((verbose > 1) ? lineno : -1));
299 + else if (a->do_regex)
300 + qgrep_print_matching_line_regcolor(a->buf_list, label,
301 + ((verbose > 1) ? lineno : -1), &a->preg);
302 + else
303 + qgrep_print_matching_line_strcolor(a->buf_list, label,
304 + ((verbose > 1) ? lineno : -1), a->strfunc,
305 + a->query);
306 + } else {
307 + /* in verbose do_list mode, list the file once
308 + * per match */
309 + printf("%s", label);
310 + if (verbose > 1)
311 + printf(":%d", lineno);
312 + putchar('\n');
313 + }
314 + /* init count down of trailing context lines */
315 + remaining_after_context = a->num_lines_after;
316 + continue;
317 +
318 +print_after_context:
319 + /* print some trailing context lines when needed */
320 + if (!remaining_after_context) {
321 + if (!status)
322 + /* we're getting closer to the need of a
323 + * separator between current match block and
324 + * the next one */
325 + ++need_separator;
326 + } else {
327 + qgrep_print_context_line(a->buf_list, label,
328 + ((verbose > 1) ? lineno : -1));
329 + --remaining_after_context;
330 + }
331 + }
332 + fclose(newfp);
333 + if (per_file_output) {
334 + /* matches were already displayed, line per line */
335 + if (a->do_count && count) {
336 + if (label != NULL)
337 + /* -c without -v/-N/-H only outputs
338 + * the matches count of the file */
339 + printf("%s:", label);
340 + printf("%d\n", count);
341 + } else if ((count && !a->invert_list) ||
342 + (!count && a->invert_list))
343 + {
344 + printf("%s\n", label);
345 + }
346 + /* do_list == 1, or we wouldn't be here */
347 + }
348 +
349 + return status;
350 +}
351 +
352 +static int
353 +qgrep_cache_cb(cache_pkg_ctx *pkg_ctx, void *priv)
354 +{
355 + struct qgrep_grepargs *data = (struct qgrep_grepargs *)priv;
356 + char buf[_Q_PATH_MAX];
357 + char name[_Q_PATH_MAX];
358 + char *label;
359 + depend_atom *patom = NULL;
360 + cache_ctx *cctx;
361 + int ret;
362 + int pfd;
363 +
364 + snprintf(buf, sizeof(buf), "%s/%s",
365 + pkg_ctx->cat_ctx->name, pkg_ctx->name);
366 + patom = atom_explode(buf);
367 + if (patom == NULL)
368 + return EXIT_FAILURE;
369 +
370 + if (data->include_atoms != NULL) {
371 + depend_atom **d;
372 + for (d = data->include_atoms; *d != NULL; d++) {
373 + if (atom_compare(patom, *d) == EQUAL)
374 + break;
375 + }
376 + if (*d == NULL) {
377 + atom_implode(patom);
378 + return EXIT_FAILURE;
379 + }
380 + }
381 +
382 + /* need to construct path in portdir to ebuild, pass it to grep */
383 + cctx = (cache_ctx *)(pkg_ctx->cat_ctx->ctx);
384 + if (cctx->cachetype == CACHE_EBUILD) {
385 + pfd = cctx->dir_ctx->vdb_fd;
386 + } else {
387 + pfd = openat(cctx->dir_ctx->vdb_fd, "../..", O_RDONLY|O_CLOEXEC);
388 + }
389 +
390 + /* cat/pkg/pkg-ver.ebuild */
391 + snprintf(buf, sizeof(buf), "%s/%s/%s.ebuild",
392 + patom->CATEGORY, patom->PN, patom->P);
393 +
394 + label = NULL;
395 + if (data->show_name) {
396 + snprintf(name, sizeof(name), "%s/%s", patom->CATEGORY, patom->P);
397 + label = name;
398 + } else if (data->show_filename) {
399 + label = buf;
400 + }
401 +
402 + ret = qgrep_grepat(pfd, buf, label, data);
403 +
404 + atom_implode(patom);
405 +
406 + return ret;
407 +}
408 +
409 +static int
410 +qgrep_vdb_cb(q_vdb_pkg_ctx *pkg_ctx, void *priv)
411 {
412 - struct dirent *pkg_dirent = NULL;
413 - if (*cat_dirent_pt == NULL || *cat_dir_pt == NULL)
414 - goto get_next_category;
415 -get_next_ebuild_from_category:
416 - if ((pkg_dirent = readdir(*cat_dir_pt)) == NULL)
417 - goto get_next_category;
418 - if (pkg_dirent->d_name[0] == '.')
419 - goto get_next_ebuild_from_category;
420 - snprintf(ebuild_path, ebuild_path_len, "%s/%s/%s.ebuild",
421 - (*cat_dirent_pt)->d_name, pkg_dirent->d_name, pkg_dirent->d_name);
422 - return ebuild_path;
423 -get_next_category:
424 - if (*cat_dir_pt != NULL)
425 - closedir(*cat_dir_pt);
426 - *cat_dirent_pt = q_vdb_get_next_dir(vdb_dir);
427 - if (*cat_dirent_pt == NULL)
428 - return NULL;
429 - if ((*cat_dir_pt = opendir((*cat_dirent_pt)->d_name)) == NULL)
430 - goto get_next_category;
431 - goto get_next_ebuild_from_category;
432 + struct qgrep_grepargs *data = (struct qgrep_grepargs *)priv;
433 + char buf[_Q_PATH_MAX];
434 + char name[_Q_PATH_MAX];
435 + char *label;
436 + depend_atom *patom = NULL;
437 + int ret;
438 + int pfd;
439 +
440 + snprintf(buf, sizeof(buf), "%s/%s",
441 + pkg_ctx->cat_ctx->name, pkg_ctx->name);
442 + patom = atom_explode(buf);
443 + if (patom == NULL)
444 + return EXIT_FAILURE;
445 +
446 + if (data->include_atoms != NULL) {
447 + depend_atom **d;
448 + for (d = data->include_atoms; *d != NULL; d++) {
449 + if (atom_compare(patom, *d) == EQUAL)
450 + break;
451 + }
452 + if (*d == NULL) {
453 + atom_implode(patom);
454 + return EXIT_FAILURE;
455 + }
456 + }
457 +
458 + /* get path to portdir */
459 + pfd = openat(pkg_ctx->cat_ctx->ctx->portroot_fd,
460 + data->portdir, O_RDONLY|O_CLOEXEC);
461 +
462 + /* cat/pkg/pkg-ver.ebuild */
463 + snprintf(buf, sizeof(buf), "%s/%s/%s.ebuild",
464 + patom->CATEGORY, patom->PN, patom->P);
465 +
466 + label = NULL;
467 + if (data->show_name) {
468 + snprintf(name, sizeof(name), "%s/%s", patom->CATEGORY, patom->P);
469 + label = name;
470 + } else if (data->show_filename) {
471 + label = buf;
472 + }
473 +
474 + ret = qgrep_grepat(pfd, buf, label, data);
475 +
476 + atom_implode(patom);
477 +
478 + return ret;
479 }
480
481 int qgrep_main(int argc, char **argv)
482 {
483 int i;
484 - int count = 0;
485 char *p;
486 - char do_count, do_regex, do_eclass, do_installed, do_list;
487 - char show_filename, skip_comments, invert_list, show_name;
488 - char per_file_output;
489 - FILE *fp = NULL;
490 + bool do_eclass;
491 + bool do_installed;
492 DIR *eclass_dir = NULL;
493 - DIR *vdb_dir = NULL;
494 - DIR *cat_dir = NULL;
495 struct dirent *dentry = NULL;
496 - char ebuild[_Q_PATH_MAX * 4];
497 - char name[_Q_PATH_MAX * 2];
498 - char *label;
499 int reflags = 0;
500 - char invert_match = 0;
501 - regex_t preg, skip_preg;
502 - char *skip_pattern = NULL;
503 - depend_atom** include_atoms = NULL;
504 unsigned long int context_optarg;
505 - char num_lines_before = 0;
506 - char num_lines_after = 0;
507 - qgrep_buf_t *buf_list;
508 - int need_separator = 0;
509 char status = 1;
510 + size_t n;
511 + char *overlay;
512
513 - QGREP_STR_FUNC strfunc = strstr;
514 -
515 - do_count = do_regex = do_eclass = do_installed = do_list = 0;
516 - show_filename = skip_comments = invert_list = show_name = 0;
517 + struct qgrep_grepargs args = {
518 + .do_count = 0,
519 + .do_regex = 0,
520 + .do_list = 0,
521 + .show_filename = 0,
522 + .show_name = 0,
523 + .skip_comments = 0,
524 + .invert_list = 0,
525 + .invert_match = 0,
526 + .skip_pattern = NULL,
527 + .num_lines_before = 0,
528 + .num_lines_after = 0,
529 + .buf_list = NULL,
530 + .query = NULL,
531 + .strfunc = strstr,
532 + .include_atoms = NULL,
533 + .portdir = NULL,
534 + };
535 +
536 + do_eclass = do_installed = 0;
537
538 while ((i = GETOPT_LONG(QGREP, qgrep, "")) != -1) {
539 switch (i) {
540 - case 'I': invert_match = 1; break;
541 + case 'I': args.invert_match = 1; break;
542 case 'i':
543 - strfunc = strcasestr;
544 + args.strfunc = strcasestr;
545 reflags |= REG_ICASE;
546 break;
547 - case 'c': do_count = 1; break;
548 - case 'l': do_list = 1; break;
549 - case 'L': do_list = invert_list = 1; break;
550 - case 'e': do_regex = 1; break;
551 + case 'c': args.do_count = 1; break;
552 + case 'l': args.do_list = 1; break;
553 + case 'L': args.do_list = args.invert_list = 1; break;
554 + case 'e': args.do_regex = 1; break;
555 case 'x':
556 - do_regex = 1;
557 + args.do_regex = 1;
558 reflags |= REG_EXTENDED;
559 break;
560 case 'J': do_installed = 1; break;
561 case 'E': do_eclass = 1; break;
562 - case 'H': show_filename = 1; break;
563 - case 'N': show_name = 1; break;
564 - case 's': skip_comments = 1; break;
565 - case 'S': skip_pattern = optarg; break;
566 + case 'H': args.show_filename = 1; break;
567 + case 'N': args.show_name = 1; break;
568 + case 's': args.skip_comments = 1; break;
569 + case 'S': args.skip_pattern = optarg; break;
570 case 'B':
571 case 'A':
572 errno = 0;
573 @@ -317,9 +553,9 @@ int qgrep_main(int argc, char **argv)
574 if (context_optarg > 254)
575 err("%s: silly value!", optarg);
576 if (i == 'B')
577 - num_lines_before = context_optarg;
578 + args.num_lines_before = context_optarg;
579 else
580 - num_lines_after = context_optarg;
581 + args.num_lines_after = context_optarg;
582 break;
583 COMMON_GETOPTS_CASES(qgrep)
584 }
585 @@ -327,312 +563,140 @@ int qgrep_main(int argc, char **argv)
586 if (argc == optind)
587 qgrep_usage(EXIT_FAILURE);
588
589 - if (do_list && do_count) {
590 + if (args.do_list && args.do_count) {
591 warn("%s and --count are incompatible options. The former wins.",
592 - (invert_list ? "--invert-list" : "--list"));
593 - do_count = 0;
594 + (args.invert_list ? "--invert-list" : "--list"));
595 + args.do_count = false;
596 }
597
598 - if (show_name && show_filename) {
599 + if (args.show_name && args.show_filename) {
600 warn("--with-name and --with-filename are incompatible options. "
601 "The former wins.");
602 - show_filename = 0;
603 + args.show_filename = false;
604 }
605
606 - if (do_list && num_lines_before) {
607 + if (args.do_list && args.num_lines_before) {
608 warn("%s and --before are incompatible options. The former wins.",
609 - (invert_list ? "--invert-list" : "--list"));
610 - num_lines_before = 0;
611 + (args.invert_list ? "--invert-list" : "--list"));
612 + args.num_lines_before = 0;
613 }
614
615 - if (do_list && num_lines_after) {
616 + if (args.do_list && args.num_lines_after) {
617 warn("%s and --after are incompatible options. The former wins.",
618 - (invert_list ? "--invert-list" : "--list"));
619 - num_lines_after = 0;
620 + (args.invert_list ? "--invert-list" : "--list"));
621 + args.num_lines_after = 0;
622 }
623
624 - if (do_count && num_lines_before) {
625 + if (args.do_count && args.num_lines_before) {
626 warn("--count and --before are incompatible options. The former wins.");
627 - num_lines_before = 0;
628 + args.num_lines_before = 0;
629 }
630
631 - if (do_count && num_lines_after) {
632 + if (args.do_count && args.num_lines_after) {
633 warn("--count and --after are incompatible options. The former wins.");
634 - num_lines_after = 0;
635 + args.num_lines_after = 0;
636 }
637
638 if (do_installed && do_eclass) {
639 warn("--installed and --eclass are incompatible options. "
640 "The former wins.");
641 - do_eclass = 0;
642 + do_eclass = false;
643 }
644
645 - /* do we report results once per file or per line ? */
646 - per_file_output = do_count || (do_list && (!verbose || invert_list));
647 - /* label for prefixing matching lines or listing matching files */
648 - label = (show_name ? name :
649 - ((verbose || show_filename || do_list) ? ebuild : NULL));
650 -
651 if (argc > (optind + 1)) {
652 - include_atoms = xcalloc(sizeof(depend_atom*), (argc - optind - 1));
653 - for (i = (optind + 1); i < argc; i++)
654 - if ((include_atoms[i - optind - 1] = atom_explode(argv[i])) == NULL)
655 + depend_atom **d = args.include_atoms =
656 + xcalloc(sizeof(depend_atom *), (argc - optind - 1) + 1);
657 + for (i = (optind + 1); i < argc; i++) {
658 + *d = atom_explode(argv[i]);
659 + if (*d == NULL) {
660 warn("%s: invalid atom, will be ignored", argv[i]);
661 + } else {
662 + d++;
663 + }
664 + }
665 + *d = NULL;
666 }
667
668 + /* make it easier to see what needs to be printed */
669 + if (!args.show_name && (verbose || args.do_list))
670 + args.show_filename = true;
671 +
672 /* pre-compile regexps once for all */
673 - if (do_regex) {
674 - if (invert_match || *RED == '\0')
675 + if (args.do_regex) {
676 + if (args.invert_match || *RED == '\0')
677 reflags |= REG_NOSUB;
678 - xregcomp(&preg, argv[optind], reflags);
679 + xregcomp(&args.preg, argv[optind], reflags);
680 reflags |= REG_NOSUB;
681 - if (skip_pattern)
682 - xregcomp(&skip_preg, skip_pattern, reflags);
683 + if (args.skip_pattern)
684 + xregcomp(&args.skip_preg, args.skip_pattern, reflags);
685 }
686 + args.query = argv[optind];
687
688 /* allocate a circular buffers list for --before */
689 - buf_list = qgrep_buf_list_alloc(num_lines_before + 1);
690 + args.buf_list = qgrep_buf_list_alloc(args.num_lines_before + 1);
691
692 - size_t n;
693 - char *overlay;
694 array_for_each(overlays, n, overlay) {
695 + args.portdir = overlay;
696 + if (do_eclass) {
697 + char buf[_Q_PATH_MAX];
698 + char name[_Q_PATH_MAX];
699 + char *label;
700 + int efd;
701
702 - /* go look either in ebuilds or eclasses or VDB */
703 - /* FIXME: use libq/vdb and libq/cache here */
704 - if (!do_eclass && !do_installed) {
705 - /* TODO: use libq/cache here */ continue;
706 - } else if (do_eclass) {
707 - xchdir(overlay);
708 - if ((eclass_dir = opendir("eclass")) == NULL) {
709 + snprintf(buf, sizeof(buf), "%s/%s/eclass", portroot, overlay);
710 + efd = open(buf, O_RDONLY|O_CLOEXEC);
711 + if (efd == -1 || (eclass_dir = fdopendir(efd)) == NULL) {
712 if (errno != ENOENT)
713 warnp("opendir(\"%s/eclass\") failed", overlay);
714 continue;
715 }
716 - } else { /* if (do_install) */
717 - /* TODO: use libq/vdb here */
718 - char buf[_Q_PATH_MAX];
719 - snprintf(buf, sizeof(buf), "%s/%s", portroot, portvdb);
720 - xchdir(buf);
721 - if ((vdb_dir = opendir(".")) == NULL)
722 - errp("could not opendir(%s/%s) for ROOT/VDB",
723 - portroot, portvdb);
724 - }
725 -
726 - /* iteration is either over ebuilds or eclasses */
727 - while (do_eclass
728 - ? ((dentry = readdir(eclass_dir))
729 - && snprintf(ebuild, sizeof(ebuild),
730 - "eclass/%s", dentry->d_name))
731 - : (do_installed
732 - ? (get_next_installed_ebuild(ebuild, sizeof(ebuild),
733 - vdb_dir, &dentry, &cat_dir) != NULL)
734 - : (fgets(ebuild, sizeof(ebuild), fp) != NULL)))
735 - {
736 - FILE *newfp;
737 -
738 - /* filter badly named files, prepare eclass or package name, etc. */
739 - if (do_eclass) {
740 - if ((p = strrchr(ebuild, '.')) == NULL)
741 - continue;
742 - if (strcmp(p, ".eclass"))
743 + while ((dentry = readdir(eclass_dir)) != NULL) {
744 + if (strstr(dentry->d_name, ".eclass") == NULL)
745 continue;
746 - if (show_name || (include_atoms != NULL)) {
747 - /* cut ".eclass" */
748 - *p = '\0';
749 - /* and skip "eclass/" */
750 - snprintf(name, sizeof(name), "%s", ebuild + 7);
751 - /* restore the filepath */
752 - *p = '.';
753 - }
754 - } else {
755 - if ((p = strchr(ebuild, '\n')) != NULL)
756 - *p = '\0';
757 - if (show_name || (include_atoms != NULL)) {
758 - size_t l;
759 - /* cut ".ebuild" */
760 - if (p == NULL)
761 - p = ebuild + strlen(ebuild);
762 - *(p-7) = '\0';
763 - /* cut "/foo/" from "cat/foo/foo-x.y" */
764 - if ((p = strchr(ebuild, '/')) == NULL)
765 - continue;
766 - *(p++) = '\0';
767 - /* find head of the ebuild basename */
768 - if ((p = strchr(p, '/')) == NULL)
769 - continue;
770 - /* find start of the pkg name, break up in two to
771 - * avoid warning about possible truncation (very
772 - * unlikely) */
773 - l = snprintf(name, sizeof(name), "%s", ebuild);
774 - snprintf(name + l, sizeof(name) - l, "%s", p);
775 - /* restore the filepath */
776 - *p = '/';
777 - *(p + strlen(p)) = '.';
778 - ebuild[strlen(ebuild)] = '/';
779 - }
780 - }
781 -
782 - /* filter the files we grep when there are extra args */
783 - if (include_atoms != NULL)
784 - if (!qgrep_name_match(name, (argc - optind - 1), include_atoms))
785 - continue;
786 -
787 - if ((newfp = fopen(ebuild, "r")) != NULL) {
788 - int lineno = 0;
789 - char remaining_after_context = 0;
790 - count = 0;
791 - /* if there have been some matches already, then a
792 - * separator will be needed */
793 - need_separator =
794 - !status && (num_lines_before || num_lines_after);
795 - /* whatever is in the circular buffers list is no more a
796 - * valid context */
797 - qgrep_buf_list_invalidate(buf_list);
798 -
799 - /* reading a new line always happen in the next buffer
800 - * of the list */
801 - while ((buf_list = buf_list->next) &&
802 - fgets(buf_list->buf, sizeof(buf_list->buf), newfp))
803 - {
804 - lineno++;
805 - buf_list->valid = 1;
806 -
807 - /* cleanup EOL */
808 - if ((p = strrchr(buf_list->buf, '\n')) != NULL)
809 - *p = 0;
810 - if ((p = strrchr(buf_list->buf, '\r')) != NULL)
811 - *p = 0;
812 -
813 - if (skip_comments) {
814 - /* reject comments line ("^[ \t]*#") */
815 - p = buf_list->buf;
816 - while (*p == ' ' || *p == '\t') p++;
817 - if (*p == '#')
818 - goto print_after_context;
819 + /* filter the files we grep when there are extra args */
820 + if (args.include_atoms != NULL) {
821 + depend_atom **d;
822 + for (d = args.include_atoms; *d != NULL; d++) {
823 + if ((*d)->PN != NULL && strncmp(dentry->d_name,
824 + (*d)->PN, strlen((*d)->PN)) == 0)
825 + break;
826 }
827 -
828 - if (skip_pattern) {
829 - /* reject some other lines which match an
830 - * optional pattern */
831 - if (!do_regex) {
832 - if (strfunc(buf_list->buf, skip_pattern) != NULL)
833 - goto print_after_context;
834 - } else {
835 - if (regexec(&skip_preg, buf_list->buf,
836 - 0, NULL, 0) == 0)
837 - goto print_after_context;
838 - }
839 - }
840 -
841 - /* four ways to match a line (with/without inversion
842 - * and regexp) */
843 - if (!invert_match) {
844 - if (do_regex == 0) {
845 - if (strfunc(buf_list->buf, argv[optind]) == NULL)
846 - goto print_after_context;
847 - } else {
848 - if (regexec(&preg, buf_list->buf, 0, NULL, 0) != 0)
849 - goto print_after_context;
850 - }
851 - } else {
852 - if (do_regex == 0) {
853 - if (strfunc(buf_list->buf, argv[optind]) != NULL)
854 - goto print_after_context;
855 - } else {
856 - if (regexec(&preg, buf_list->buf, 0, NULL, 0) == 0)
857 - goto print_after_context;
858 - }
859 - }
860 -
861 - count++;
862 - status = 0; /* got a match, exit status should be 0 */
863 - if (per_file_output)
864 + if (*d == NULL)
865 continue;
866 - /* matching files are listed out of this loop */
867 -
868 - if ((need_separator > 0)
869 - && (num_lines_before || num_lines_after))
870 - printf("--\n");
871 - /* "need_separator" is not a flag, but a counter, so that
872 - * adjacent contextes are not separated */
873 - need_separator = 0 - num_lines_before;
874 - if (!do_list) {
875 - /* print the leading context */
876 - qgrep_print_before_context(buf_list,
877 - num_lines_before, label,
878 - ((verbose > 1) ? lineno : -1));
879 - /* print matching line */
880 - if (invert_match || *RED == '\0')
881 - qgrep_print_matching_line_nocolor(buf_list, label,
882 - ((verbose > 1) ? lineno : -1));
883 - else if (do_regex)
884 - qgrep_print_matching_line_regcolor(buf_list, label,
885 - ((verbose > 1) ? lineno : -1), &preg);
886 - else
887 - qgrep_print_matching_line_strcolor(buf_list, label,
888 - ((verbose > 1) ? lineno : -1), strfunc,
889 - argv[optind]);
890 - } else {
891 - /* in verbose do_list mode, list the file once
892 - * per match */
893 - printf("%s", label);
894 - if (verbose > 1)
895 - printf(":%d", lineno);
896 - putchar('\n');
897 - }
898 - /* init count down of trailing context lines */
899 - remaining_after_context = num_lines_after;
900 - continue;
901 + }
902
903 - print_after_context:
904 - /* print some trailing context lines when needed */
905 - if (!remaining_after_context) {
906 - if (!status)
907 - /* we're getting closer to the need of a
908 - * separator between current match block and
909 - * the next one */
910 - ++need_separator;
911 - } else {
912 - qgrep_print_context_line(buf_list, label,
913 - ((verbose > 1) ? lineno : -1));
914 - --remaining_after_context;
915 - }
916 + label = NULL;
917 + if (args.show_name) {
918 + snprintf(name, sizeof(name), "%.*s",
919 + (int)(strlen(dentry->d_name) - 7), dentry->d_name);
920 + label = name;
921 + } else if (args.show_filename) {
922 + snprintf(name, sizeof(name), "eclass/%s", dentry->d_name);
923 + label = name;
924 }
925 - fclose(newfp);
926 - if (!per_file_output)
927 - continue;
928 - /* matches were already displayed, line per line */
929 - if (do_count && count) {
930 - if (label != NULL)
931 - /* -c without -v/-N/-H only outputs
932 - * the matches count of the file */
933 - printf("%s:", label);
934 - printf("%d\n", count);
935 - } else if ((count && !invert_list) || (!count && invert_list))
936 - printf("%s\n", label);
937 - /* do_list == 1, or we wouldn't be here */
938 + status = qgrep_grepat(efd, dentry->d_name, label, &args);
939 }
940 - }
941 - if (do_eclass)
942 closedir(eclass_dir);
943 - else if (!do_installed)
944 - fclose(fp);
945 -
946 - if (do_installed)
947 - break;
948 + } else if (do_installed) {
949 + status = q_vdb_foreach_pkg(portroot, portvdb,
950 + qgrep_vdb_cb, &args, NULL);
951 + } else { /* do_ebuild */
952 + status = cache_foreach_pkg(portroot, overlay,
953 + qgrep_cache_cb, &args, NULL);
954 + }
955 }
956
957 - if (do_regex)
958 - regfree(&preg);
959 - if (do_regex && skip_pattern)
960 - regfree(&skip_preg);
961 - if (include_atoms != NULL) {
962 + if (args.do_regex)
963 + regfree(&args.preg);
964 + if (args.do_regex && args.skip_pattern)
965 + regfree(&args.skip_preg);
966 + if (args.include_atoms != NULL) {
967 for (i = 0; i < (argc - optind - 1); i++)
968 - if (include_atoms[i] != NULL)
969 - atom_implode(include_atoms[i]);
970 - free(include_atoms);
971 + if (args.include_atoms[i] != NULL)
972 + atom_implode(args.include_atoms[i]);
973 + free(args.include_atoms);
974 }
975 - qgrep_buf_list_free(buf_list);
976 + qgrep_buf_list_free(args.buf_list);
977
978 return status;
979 }