Merge branch 'se/rev-parse' into next
[gitweb.git] / builtin-grep.c
index a762c48a5a3de70c47814df830aae0760549d163..fead35662944070f8502ed0484fe6b6085d7ac86 100644 (file)
@@ -98,6 +98,7 @@ struct grep_opt {
        unsigned unmatch_name_only:1;
        unsigned count:1;
        unsigned word_regexp:1;
+       unsigned fixed:1;
 #define GREP_BINARY_DEFAULT    0
 #define GREP_BINARY_NOMATCH    1
 #define GREP_BINARY_TEXT       2
@@ -179,6 +180,20 @@ static int buffer_is_binary(const char *ptr, unsigned long size)
        return 0;
 }
 
+static int fixmatch(const char *pattern, char *line, regmatch_t *match)
+{
+       char *hit = strstr(line, pattern);
+       if (!hit) {
+               match->rm_so = match->rm_eo = -1;
+               return REG_NOMATCH;
+       }
+       else {
+               match->rm_so = hit - line;
+               match->rm_eo = match->rm_so + strlen(pattern);
+               return 0;
+       }
+}
+
 static int grep_buffer(struct grep_opt *opt, const char *name,
                       char *buf, unsigned long size)
 {
@@ -224,9 +239,14 @@ static int grep_buffer(struct grep_opt *opt, const char *name,
                *eol = 0;
 
                for (p = opt->pattern_list; p; p = p->next) {
-                       regex_t *exp = &p->regexp;
-                       hit = !regexec(exp, bol, ARRAY_SIZE(pmatch),
-                                      pmatch, 0);
+                       if (!opt->fixed) {
+                               regex_t *exp = &p->regexp;
+                               hit = !regexec(exp, bol, ARRAY_SIZE(pmatch),
+                                              pmatch, 0);
+                       }
+                       else {
+                               hit = !fixmatch(p->pattern, bol, pmatch);
+                       }
 
                        if (hit && opt->word_regexp) {
                                /* Match beginning must be either
@@ -241,10 +261,10 @@ static int grep_buffer(struct grep_opt *opt, const char *name,
                                        die("regexp returned nonsense");
                                if (pmatch[0].rm_so != 0 &&
                                    word_char(bol[pmatch[0].rm_so-1]))
-                                       continue; /* not a word boundary */
-                               if ((eol-bol) < pmatch[0].rm_eo &&
+                                       hit = 0;
+                               if (pmatch[0].rm_eo != (eol-bol) &&
                                    word_char(bol[pmatch[0].rm_eo]))
-                                       continue; /* not a word boundary */
+                                       hit = 0;
                        }
                        if (hit)
                                break;
@@ -494,20 +514,30 @@ static const char builtin_grep_usage[] =
 int cmd_grep(int argc, const char **argv, char **envp)
 {
        int hit = 0;
-       int no_more_flags = 0;
        int cached = 0;
+       int seen_dashdash = 0;
        struct grep_opt opt;
        struct object_list *list, **tail, *object_list = NULL;
        const char *prefix = setup_git_directory();
        const char **paths = NULL;
+       int i;
 
        memset(&opt, 0, sizeof(opt));
        opt.pattern_tail = &opt.pattern_list;
        opt.regflags = REG_NEWLINE;
 
        /*
-        * No point using rev_info, really.
+        * If there is no -- then the paths must exist in the working
+        * tree.  If there is no explicit pattern specified with -e or
+        * -f, we take the first unrecognized non option to be the
+        * pattern, but then what follows it must be zero or more
+        * valid refs up to the -- (if exists), and then existing
+        * paths.  If there is an explicit pattern, then the first
+        * unrecocnized non option is the beginning of the refs list
+        * that continues up to the -- (if exists), and then paths.
         */
+
+       tail = &object_list;
        while (1 < argc) {
                const char *arg = argv[1];
                argc--; argv++;
@@ -539,6 +569,11 @@ int cmd_grep(int argc, const char **argv, char **envp)
                        opt.regflags |= REG_EXTENDED;
                        continue;
                }
+               if (!strcmp("-F", arg) ||
+                   !strcmp("--fixed-strings", arg)) {
+                       opt.fixed = 1;
+                       continue;
+               }
                if (!strcmp("-G", arg) ||
                    !strcmp("--basic-regexp", arg)) {
                        opt.regflags &= ~REG_EXTENDED;
@@ -618,7 +653,7 @@ int cmd_grep(int argc, const char **argv, char **envp)
                                usage(builtin_grep_usage);
                        patterns = fopen(argv[1], "r");
                        if (!patterns)
-                               die("'%s': %s", strerror(errno));
+                               die("'%s': %s", argv[1], strerror(errno));
                        while (fgets(buf, sizeof(buf), patterns)) {
                                int len = strlen(buf);
                                if (buf[len-1] == '\n')
@@ -642,47 +677,64 @@ int cmd_grep(int argc, const char **argv, char **envp)
                        }
                        usage(builtin_grep_usage);
                }
-               if (!strcmp("--", arg)) {
-                       no_more_flags = 1;
-                       continue;
-               }
-               /* Either unrecognized option or a single pattern */
-               if (!no_more_flags && *arg == '-')
+               if (!strcmp("--", arg))
+                       break;
+               if (*arg == '-')
                        usage(builtin_grep_usage);
+
+               /* First unrecognized non-option token */
                if (!opt.pattern_list) {
                        add_pattern(&opt, arg, "command line", 0);
                        break;
                }
                else {
                        /* We are looking at the first path or rev;
-                        * it is found at argv[0] after leaving the
+                        * it is found at argv[1] after leaving the
                         * loop.
                         */
                        argc++; argv--;
                        break;
                }
        }
+
        if (!opt.pattern_list)
                die("no pattern given.");
-       compile_patterns(&opt);
-       tail = &object_list;
-       while (1 < argc) {
-               struct object *object;
-               struct object_list *elem;
-               const char *arg = argv[1];
+       if ((opt.regflags != REG_NEWLINE) && opt.fixed)
+               die("cannot mix --fixed-strings and regexp");
+       if (!opt.fixed)
+               compile_patterns(&opt);
+
+       /* Check revs and then paths */
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
                unsigned char sha1[20];
-               if (get_sha1(arg, sha1) < 0)
-                       break;
-               object = parse_object(sha1);
-               if (!object)
-                       die("bad object %s", arg);
-               elem = object_list_insert(object, tail);
-               elem->name = arg;
-               tail = &elem->next;
-               argc--; argv++;
+               /* Is it a rev? */
+               if (!get_sha1(arg, sha1)) {
+                       struct object *object = parse_object(sha1);
+                       struct object_list *elem;
+                       if (!object)
+                               die("bad object %s", arg);
+                       elem = object_list_insert(object, tail);
+                       elem->name = arg;
+                       tail = &elem->next;
+                       continue;
+               }
+               if (!strcmp(arg, "--")) {
+                       i++;
+                       seen_dashdash = 1;
+               }
+               break;
        }
-       if (1 < argc)
-               paths = get_pathspec(prefix, argv + 1);
+
+       /* The rest are paths */
+       if (!seen_dashdash) {
+               int j;
+               for (j = i; j < argc; j++)
+                       verify_filename(prefix, argv[j]);
+       }
+
+       if (i < argc)
+               paths = get_pathspec(prefix, argv + i);
        else if (prefix) {
                paths = xcalloc(2, sizeof(const char *));
                paths[0] = prefix;