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 |
} |