summaryrefslogtreecommitdiff
path: root/grep.c
diff options
context:
space:
mode:
Diffstat (limited to 'grep.c')
-rw-r--r--grep.c100
1 files changed, 86 insertions, 14 deletions
diff --git a/grep.c b/grep.c
index bb548cae69..c668034739 100644
--- a/grep.c
+++ b/grep.c
@@ -2,6 +2,8 @@
#include "grep.h"
#include "userdiff.h"
#include "xdiff-interface.h"
+#include "diff.h"
+#include "diffcore.h"
static int grep_source_load(struct grep_source *gs);
static int grep_source_is_binary(struct grep_source *gs);
@@ -1322,6 +1324,58 @@ static void std_output(struct grep_opt *opt, const void *buf, size_t size)
fwrite(buf, size, 1, stdout);
}
+static int fill_textconv_grep(struct userdiff_driver *driver,
+ struct grep_source *gs)
+{
+ struct diff_filespec *df;
+ char *buf;
+ size_t size;
+
+ if (!driver || !driver->textconv)
+ return grep_source_load(gs);
+
+ /*
+ * The textconv interface is intimately tied to diff_filespecs, so we
+ * have to pretend to be one. If we could unify the grep_source
+ * and diff_filespec structs, this mess could just go away.
+ */
+ df = alloc_filespec(gs->path);
+ switch (gs->type) {
+ case GREP_SOURCE_SHA1:
+ fill_filespec(df, gs->identifier, 1, 0100644);
+ break;
+ case GREP_SOURCE_FILE:
+ fill_filespec(df, null_sha1, 0, 0100644);
+ break;
+ default:
+ die("BUG: attempt to textconv something without a path?");
+ }
+
+ /*
+ * fill_textconv is not remotely thread-safe; it may load objects
+ * behind the scenes, and it modifies the global diff tempfile
+ * structure.
+ */
+ grep_read_lock();
+ size = fill_textconv(driver, df, &buf);
+ grep_read_unlock();
+ free_filespec(df);
+
+ /*
+ * The normal fill_textconv usage by the diff machinery would just keep
+ * the textconv'd buf separate from the diff_filespec. But much of the
+ * grep code passes around a grep_source and assumes that its "buf"
+ * pointer is the beginning of the thing we are searching. So let's
+ * install our textconv'd version into the grep_source, taking care not
+ * to leak any existing buffer.
+ */
+ grep_source_clear_data(gs);
+ gs->buf = buf;
+ gs->size = size;
+
+ return 0;
+}
+
static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int collect_hits)
{
char *bol;
@@ -1332,6 +1386,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
unsigned count = 0;
int try_lookahead = 0;
int show_function = 0;
+ struct userdiff_driver *textconv = NULL;
enum grep_context ctx = GREP_CONTEXT_HEAD;
xdemitconf_t xecfg;
@@ -1353,19 +1408,36 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
}
opt->last_shown = 0;
- switch (opt->binary) {
- case GREP_BINARY_DEFAULT:
- if (grep_source_is_binary(gs))
- binary_match_only = 1;
- break;
- case GREP_BINARY_NOMATCH:
- if (grep_source_is_binary(gs))
- return 0; /* Assume unmatch */
- break;
- case GREP_BINARY_TEXT:
- break;
- default:
- die("bug: unknown binary handling mode");
+ if (opt->allow_textconv) {
+ grep_source_load_driver(gs);
+ /*
+ * We might set up the shared textconv cache data here, which
+ * is not thread-safe.
+ */
+ grep_attr_lock();
+ textconv = userdiff_get_textconv(gs->driver);
+ grep_attr_unlock();
+ }
+
+ /*
+ * We know the result of a textconv is text, so we only have to care
+ * about binary handling if we are not using it.
+ */
+ if (!textconv) {
+ switch (opt->binary) {
+ case GREP_BINARY_DEFAULT:
+ if (grep_source_is_binary(gs))
+ binary_match_only = 1;
+ break;
+ case GREP_BINARY_NOMATCH:
+ if (grep_source_is_binary(gs))
+ return 0; /* Assume unmatch */
+ break;
+ case GREP_BINARY_TEXT:
+ break;
+ default:
+ die("bug: unknown binary handling mode");
+ }
}
memset(&xecfg, 0, sizeof(xecfg));
@@ -1373,7 +1445,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
try_lookahead = should_lookahead(opt);
- if (grep_source_load(gs) < 0)
+ if (fill_textconv_grep(textconv, gs) < 0)
return 0;
bol = gs->buf;