Merge branch 'jc/bindiff' into next
[gitweb.git] / builtin-grep.c
index 2124fa62e8900754615bf7eb49228a4d2b790d52..c89ee33a1ede4e032cae850315ee2b2185a64457 100644 (file)
@@ -93,8 +93,13 @@ struct grep_opt {
        unsigned linenum:1;
        unsigned invert:1;
        unsigned name_only:1;
+       unsigned unmatch_name_only:1;
        unsigned count:1;
        unsigned word_regexp:1;
+#define GREP_BINARY_DEFAULT    0
+#define GREP_BINARY_NOMATCH    1
+#define GREP_BINARY_TEXT       2
+       unsigned binary:2;
        int regflags;
        unsigned pre_context;
        unsigned post_context;
@@ -148,6 +153,19 @@ static void show_line(struct grep_opt *opt, const char *bol, const char *eol,
        printf("%.*s\n", (int)(eol-bol), bol);
 }
 
+/*
+ * NEEDSWORK: share code with diff.c
+ */
+#define FIRST_FEW_BYTES 8000
+static int buffer_is_binary(const char *ptr, unsigned long size)
+{
+       if (FIRST_FEW_BYTES < size)
+               size = FIRST_FEW_BYTES;
+       if (memchr(ptr, 0, size))
+               return 1;
+       return 0;
+}
+
 static int grep_buffer(struct grep_opt *opt, const char *name,
                       char *buf, unsigned long size)
 {
@@ -160,9 +178,23 @@ static int grep_buffer(struct grep_opt *opt, const char *name,
        } *prev = NULL, *pcl;
        unsigned last_hit = 0;
        unsigned last_shown = 0;
+       int binary_match_only = 0;
        const char *hunk_mark = "";
        unsigned count = 0;
 
+       if (buffer_is_binary(buf, size)) {
+               switch (opt->binary) {
+               case GREP_BINARY_DEFAULT:
+                       binary_match_only = 1;
+                       break;
+               case GREP_BINARY_NOMATCH:
+                       return 0; /* Assume unmatch */
+                       break;
+               default:
+                       break;
+               }
+       }
+
        if (opt->pre_context)
                prev = xcalloc(opt->pre_context, sizeof(*prev));
        if (opt->pre_context || opt->post_context)
@@ -210,8 +242,17 @@ static int grep_buffer(struct grep_opt *opt, const char *name,
                 */
                if (opt->invert)
                        hit = !hit;
+               if (opt->unmatch_name_only) {
+                       if (hit)
+                               return 0;
+                       goto next_line;
+               }
                if (hit) {
                        count++;
+                       if (binary_match_only) {
+                               printf("Binary file %s matches\n", name);
+                               return 1;
+                       }
                        if (opt->name_only) {
                                printf("%s\n", name);
                                return 1;
@@ -262,11 +303,22 @@ static int grep_buffer(struct grep_opt *opt, const char *name,
                        prev->bol = bol;
                        prev->eol = eol;
                }
+
+       next_line:
                *eol = ch;
                bol = eol + 1;
+               if (!left)
+                       break;
                left--;
                lno++;
        }
+
+       if (opt->unmatch_name_only) {
+               /* We did not see any hit, so we want to show this */
+               printf("%s\n", name);
+               return 1;
+       }
+
        /* NEEDSWORK:
         * The real "grep -c foo *.c" gives many "bar.c:0" lines,
         * which feels mostly useless but sometimes useful.  Maybe
@@ -451,11 +503,20 @@ int cmd_grep(int argc, const char **argv, char **envp)
                        cached = 1;
                        continue;
                }
+               if (!strcmp("-a", arg) ||
+                   !strcmp("--text", arg)) {
+                       opt.binary = GREP_BINARY_TEXT;
+                       continue;
+               }
                if (!strcmp("-i", arg) ||
                    !strcmp("--ignore-case", arg)) {
                        opt.regflags |= REG_ICASE;
                        continue;
                }
+               if (!strcmp("-I", arg)) {
+                       opt.binary = GREP_BINARY_NOMATCH;
+                       continue;
+               }
                if (!strcmp("-v", arg) ||
                    !strcmp("--invert-match", arg)) {
                        opt.invert = 1;
@@ -486,6 +547,11 @@ int cmd_grep(int argc, const char **argv, char **envp)
                        opt.name_only = 1;
                        continue;
                }
+               if (!strcmp("-L", arg) ||
+                   !strcmp("--files-without-match", arg)) {
+                       opt.unmatch_name_only = 1;
+                       continue;
+               }
                if (!strcmp("-c", arg) ||
                    !strcmp("--count", arg)) {
                        opt.count = 1;