diff options
author | Ned Ludd <solar@gentoo.org> | 2007-03-26 16:20:08 +0000 |
---|---|---|
committer | Ned Ludd <solar@gentoo.org> | 2007-03-26 16:20:08 +0000 |
commit | 29e41aff69416e9a4b07a446d337ff572d352eed (patch) | |
tree | d3d2af20171d968391ccd2d7ee027c9c18be7d9b /qgrep.c | |
parent | - Misc enhancements for qgrep. TGL bug 171374 (diff) | |
download | portage-utils-29e41aff69416e9a4b07a446d337ff572d352eed.tar.gz portage-utils-29e41aff69416e9a4b07a446d337ff572d352eed.tar.bz2 portage-utils-29e41aff69416e9a4b07a446d337ff572d352eed.zip |
- This commit adds -A/-B options for qgrep (context lines) from TGL bug 172240
Diffstat (limited to 'qgrep.c')
-rw-r--r-- | qgrep.c | 257 |
1 files changed, 227 insertions, 30 deletions
@@ -1,7 +1,7 @@ /* * Copyright 2005-2006 Gentoo Foundation * Distributed under the terms of the GNU General Public License v2 - * $Header: /var/cvsroot/gentoo-projects/portage-utils/qgrep.c,v 1.18 2007/03/26 16:17:34 solar Exp $ + * $Header: /var/cvsroot/gentoo-projects/portage-utils/qgrep.c,v 1.19 2007/03/26 16:20:08 solar Exp $ * * Copyright 2005-2006 Ned Ludd - <solar@gentoo.org> * Copyright 2005-2006 Mike Frysinger - <vapier@gentoo.org> @@ -10,7 +10,7 @@ #ifdef APPLET_qgrep -#define QGREP_FLAGS "IiHNclLexEsS:" COMMON_FLAGS +#define QGREP_FLAGS "IiHNclLexEsS:B:A:" COMMON_FLAGS static struct option const qgrep_long_opts[] = { {"invert-match", no_argument, NULL, 'I'}, {"ignore-case", no_argument, NULL, 'i'}, @@ -24,6 +24,8 @@ static struct option const qgrep_long_opts[] = { {"eclass", no_argument, NULL, 'E'}, {"skip-comments", no_argument, NULL, 's'}, {"skip", a_argument, NULL, 'S'}, + {"before-context", a_argument, NULL, 'B'}, + {"after-context", a_argument, NULL, 'A'}, COMMON_LONG_OPTS }; static const char *qgrep_opts_help[] = { @@ -39,9 +41,11 @@ static const char *qgrep_opts_help[] = { "Search in eclasses instead of ebuilds", "Skip comments lines", "Skip lines matching <arg>", + "Print <arg> lines of leading context", + "Print <arg> lines of trailing context", COMMON_OPTS_HELP }; -static const char qgrep_rcsid[] = "$Id: qgrep.c,v 1.18 2007/03/26 16:17:34 solar Exp $"; +static const char qgrep_rcsid[] = "$Id: qgrep.c,v 1.19 2007/03/26 16:20:08 solar Exp $"; #define qgrep_usage(ret) usage(ret, QGREP_FLAGS, qgrep_long_opts, qgrep_opts_help, lookup_applet_idx("qgrep")) char qgrep_name_match(const char*, const int, depend_atom**); @@ -73,6 +77,91 @@ char qgrep_name_match(const char* name, const int argc, depend_atom** argv) return 0; } +/* Circular list of line buffers for --before-context */ +typedef struct qgrep_buf { + char valid; + /* 1 when the line should be included in + * a leading context, and 0 when it has already + * been displayed, or is from a previous file. */ + char buf[BUFSIZ]; + struct qgrep_buf *next; +} qgrep_buf_t; + +/* Allocate <length> buffers in a circular list. + * <length> must be at least 1. */ +qgrep_buf_t* qgrep_buf_list_alloc(const char); +qgrep_buf_t* qgrep_buf_list_alloc(const char length) +{ + char i; + qgrep_buf_t *head, *current; + current = head = xmalloc(sizeof(qgrep_buf_t)); + for (i = 1; i < length; i++) { + current->next = xmalloc(sizeof(qgrep_buf_t)); + current = current->next; + } + current->next = head; + return head; +} + +/* Free a circular buffers list. */ +void qgrep_buf_list_free(qgrep_buf_t *); +void qgrep_buf_list_free(qgrep_buf_t *head) +{ + qgrep_buf_t *current, *next; + next = head; + do { + current = next; + next = current->next; + free(current); + } while (next != head); +} + +/* Set valid=0 in the whole list. */ +void qgrep_buf_list_invalidate(qgrep_buf_t *); +void qgrep_buf_list_invalidate(qgrep_buf_t *head) +{ + qgrep_buf_t *current; + current = head; + do { + current->valid = 0; + current = current->next; + } while (current != head); +} + +/* Display a buffer, with an optionnal prefix. */ +void qgrep_print_line(qgrep_buf_t *, const char *, const int, const char); +void qgrep_print_line(qgrep_buf_t *current, const char *label, + const int line_number, const char zig) +{ + if (label != NULL) { + printf("%s", label); + if (line_number > 0) + printf(":%d", line_number); + putchar(zig); + } + printf("%s\n", current->buf); + current->valid = 0; +} +#define qgrep_print_context_line(buf, label, lineno) \ + qgrep_print_line(buf, label, lineno, '-') +#define qgrep_print_matching_line(buf, label, lineno) \ + qgrep_print_line(buf, label, lineno, ':') + +/* Display a leading context (valid lines of the buffers list, but the matching one). */ +void qgrep_print_before_context(qgrep_buf_t *, const char, const char *, const int); +void qgrep_print_before_context(qgrep_buf_t *current, const char num_lines_before, + const char *label, const int match_line_number) +{ + int line_number; + line_number = match_line_number - num_lines_before; + while ((current = current->next) + && (line_number < match_line_number)) { + if (current->valid) + qgrep_print_context_line(current, label, line_number); + line_number++; + } +} + int qgrep_main(int argc, char **argv) { int i; @@ -86,12 +175,18 @@ int qgrep_main(int argc, char **argv) struct dirent *dentry; char ebuild[_Q_PATH_MAX]; char name[_Q_PATH_MAX]; - char buf0[BUFSIZ]; + char *label; int reflags = REG_NOSUB; 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; typedef char *(*FUNC) (char *, char *); FUNC strfunc = (FUNC) strstr; @@ -122,6 +217,21 @@ int qgrep_main(int argc, char **argv) case 'N': show_name = 1; break; case 's': skip_comments = 1; break; case 'S': skip_pattern = optarg; break; + case 'B': + case 'A': + errno = 0; + context_optarg = strtol(optarg, &p, 10); + if (errno != 0) + errp("%s: not a valid integer", optarg); + else if (p == optarg || *p != '\0') + err("%s: not a valid integer", optarg); + if (context_optarg > 254) + err("%s: silly value!", optarg); + if (i == 'B') + num_lines_before = context_optarg; + else + num_lines_after = context_optarg; + break; COMMON_GETOPTS_CASES(qgrep) } } @@ -139,8 +249,32 @@ int qgrep_main(int argc, char **argv) show_filename = 0; } + if (do_list && num_lines_before) { + warn("%s and --before-context are incompatible options. The former wins.", + (invert_list ? "--invert-list" : "--list")); + num_lines_before = 0; + } + + if (do_list && num_lines_after) { + warn("%s and --after-context are incompatible options. The former wins.", + (invert_list ? "--invert-list" : "--list")); + num_lines_after = 0; + } + + if (do_count && num_lines_before) { + warn("--count and --before-context are incompatible options. The former wins."); + num_lines_before = 0; + } + + if (do_count && num_lines_after) { + warn("--count and --after-context are incompatible options. The former wins."); + num_lines_after = 0; + } + /* 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)); @@ -149,6 +283,7 @@ int qgrep_main(int argc, char **argv) warn("%s: invalid atom, will be ignored", argv[i]); } + /* pre-compile regexps once for all */ if (do_regex) { int ret; char err[256]; @@ -166,6 +301,7 @@ int qgrep_main(int argc, char **argv) } } + /* go look either in ebuilds or eclasses */ if (!do_eclass) { initialize_ebuild_flat(); /* sets our pwd to $PORTDIR */ if ((fp = fopen(CACHE_EBUILD_FILE, "r")) == NULL) @@ -177,11 +313,17 @@ int qgrep_main(int argc, char **argv) errp("opendir(\"%s/eclass\") failed", portdir); } + /* allocate a circular buffers list for --before-context */ + buf_list = qgrep_buf_list_alloc(num_lines_before + 1); + + /* iteration is either over ebuilds or eclasses */ while (do_eclass ? ((dentry = readdir(eclass_dir)) && snprintf(ebuild, sizeof(ebuild), "eclass/%s", dentry->d_name)) : ((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; @@ -219,68 +361,122 @@ int qgrep_main(int argc, char **argv) } } + /* 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) { unsigned 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); - while ((fgets(buf0, sizeof(buf0), newfp)) != NULL) { + /* 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)) != NULL) { lineno++; - if ((p = strrchr(buf0, '\n')) != NULL) + buf_list->valid = 1; + + /* cleanup EOL */ + if ((p = strrchr(buf_list->buf, '\n')) != NULL) *p = 0; - if ((p = strrchr(buf0, '\r')) != NULL) + if ((p = strrchr(buf_list->buf, '\r')) != NULL) *p = 0; if (skip_comments) { - p = buf0; + /* reject comments line ("^[ \t]*#") */ + p = buf_list->buf; while (*p == ' ' || *p == '\t') p++; - if (*p == '#') continue; + if (*p == '#') + goto print_after_context; } if (skip_pattern) { + /* reject some other lines which match an optional pattern */ if (!do_regex) { - if (( (FUNC *) (strfunc) (buf0, skip_pattern)) != NULL) continue; + if (( (FUNC *) (strfunc) (buf_list->buf, skip_pattern)) != NULL) + goto print_after_context; } else { - if (regexec(&skip_preg, buf0, 0, NULL, 0) == 0) continue; + 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 (( (FUNC *) (strfunc) (buf0, argv[optind])) == NULL) continue; + if (( (FUNC *) (strfunc) (buf_list->buf, argv[optind])) == NULL) + goto print_after_context; } else { - if (regexec(&preg, buf0, 0, NULL, 0) != 0) continue; + if (regexec(&preg, buf_list->buf, 0, NULL, 0) != 0) + goto print_after_context; } } else { if (do_regex == 0) { - if (( (FUNC *) (strfunc) (buf0, argv[optind])) != NULL) continue; + if (( (FUNC *) (strfunc) (buf_list->buf, argv[optind])) != NULL) + goto print_after_context; } else { - if (regexec(&preg, buf0, 0, NULL, 0) == 0) continue; + if (regexec(&preg, buf_list->buf, 0, NULL, 0) == 0) + goto print_after_context; } } count++; - if (per_file_output) continue; - if (verbose || show_filename || show_name) { - printf("%s", (show_name ? name : ebuild)); - if (verbose > 1) printf(":%d", lineno); - if (!do_list) - printf(": "); + 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) + && (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, and matching line */ + qgrep_print_before_context(buf_list, num_lines_before, label, + ((verbose > 1) ? lineno : -1)); + qgrep_print_matching_line(buf_list, label, + ((verbose > 1) ? lineno : -1)); + } 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; } - printf("%s\n", (do_list ? "" : buf0)); } fclose(newfp); - if (!per_file_output) continue; + if (!per_file_output) + continue; /* matches were already displayed, line per line */ if (do_count && count) { - if (verbose || show_filename || show_name) - printf("%s:", (show_name ? name : ebuild)); - printf("%d", count); - puts(""); - } else if (do_list && ((count && !invert_list) || (!count && invert_list))) - printf("%s\n", (show_name ? name : ebuild)); + 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 */ } } if (do_eclass) @@ -297,7 +493,8 @@ int qgrep_main(int argc, char **argv) atom_implode(include_atoms[i]); free(include_atoms); } - return EXIT_SUCCESS; + qgrep_buf_list_free(buf_list); + return status; } #else |