aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Ludd <solar@gentoo.org>2007-03-26 16:20:08 +0000
committerNed Ludd <solar@gentoo.org>2007-03-26 16:20:08 +0000
commit29e41aff69416e9a4b07a446d337ff572d352eed (patch)
treed3d2af20171d968391ccd2d7ee027c9c18be7d9b /qgrep.c
parent- Misc enhancements for qgrep. TGL bug 171374 (diff)
downloadportage-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.c257
1 files changed, 227 insertions, 30 deletions
diff --git a/qgrep.c b/qgrep.c
index 8bd1b98..0101d2d 100644
--- a/qgrep.c
+++ b/qgrep.c
@@ -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