line-range: teach -L^/RE/ to search from start of file
[gitweb.git] / line-range.c
index 8faf94374517b605d2529de2f4bafc869b72df60..70484899aca0592de3f08e31ca05d9d668ccb7ff 100644 (file)
@@ -6,6 +6,18 @@
 
 /*
  * Parse one item in the -L option
+ *
+ * 'begin' is applicable only to relative range anchors. Absolute anchors
+ * ignore this value.
+ *
+ * When parsing "-L A,B", parse_loc() is called once for A and once for B.
+ *
+ * When parsing A, 'begin' must be a negative number, the absolute value of
+ * which is the line at which relative start-of-range anchors should be
+ * based. Beginning of file is represented by -1.
+ *
+ * When parsing B, 'begin' must be the positive line number immediately
+ * following the line computed for 'A'.
  */
 static const char *parse_loc(const char *spec, nth_line_fn_t nth_line,
                             void *data, long lines, long begin, long *ret)
@@ -21,11 +33,13 @@ static const char *parse_loc(const char *spec, nth_line_fn_t nth_line,
         * for 20 lines, or "-L <something>,-5" for 5 lines ending at
         * <something>.
         */
-       if (1 < begin && (spec[0] == '+' || spec[0] == '-')) {
+       if (1 <= begin && (spec[0] == '+' || spec[0] == '-')) {
                num = strtol(spec + 1, &term, 10);
                if (term != spec + 1) {
                        if (!ret)
                                return term;
+                       if (num == 0)
+                               die("-L invalid empty range");
                        if (spec[0] == '-')
                                num = 0 - num;
                        if (0 < num)
@@ -44,6 +58,16 @@ static const char *parse_loc(const char *spec, nth_line_fn_t nth_line,
                        *ret = num;
                return term;
        }
+
+       if (begin < 0) {
+               if (spec[0] != '^')
+                       begin = -begin;
+               else {
+                       begin = 1;
+                       spec++;
+               }
+       }
+
        if (spec[0] != '/')
                return spec;
 
@@ -83,7 +107,8 @@ static const char *parse_loc(const char *spec, nth_line_fn_t nth_line,
        else {
                char errbuf[1024];
                regerror(reg_error, &regexp, errbuf, 1024);
-               die("-L parameter '%s': %s", spec + 1, errbuf);
+               die("-L parameter '%s' starting at line %ld: %s",
+                   spec + 1, begin + 1, errbuf);
        }
 }
 
@@ -208,9 +233,16 @@ static const char *parse_range_funcname(const char *arg, nth_line_fn_t nth_line_
 }
 
 int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb,
-                   void *cb_data, long lines, long *begin, long *end,
-                   const char *path)
+                   void *cb_data, long lines, long anchor,
+                   long *begin, long *end, const char *path)
 {
+       *begin = *end = 0;
+
+       if (anchor < 1)
+               anchor = 1;
+       if (anchor > lines)
+               anchor = lines + 1;
+
        if (*arg == ':') {
                arg = parse_range_funcname(arg, nth_line_cb, cb_data, lines, begin, end, path);
                if (!arg || *arg)
@@ -218,7 +250,7 @@ int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb,
                return 0;
        }
 
-       arg = parse_loc(arg, nth_line_cb, cb_data, lines, 1, begin);
+       arg = parse_loc(arg, nth_line_cb, cb_data, lines, -anchor, begin);
 
        if (*arg == ',')
                arg = parse_loc(arg + 1, nth_line_cb, cb_data, lines, *begin + 1, end);
@@ -226,6 +258,11 @@ int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb,
        if (*arg)
                return -1;
 
+       if (*begin && *end && *end < *begin) {
+               long tmp;
+               tmp = *end; *end = *begin; *begin = tmp;
+       }
+
        return 0;
 }