ref-filter: add an 'rstrip=<N>' option to atoms which deal with refnames
[gitweb.git] / ref-filter.c
index 7ff284cdf17c8562f627df9700c34153ac48192f..47e292bc46105544d5342a7e1ebe61b8cfdd36e8 100644 (file)
@@ -33,8 +33,8 @@ struct if_then_else {
 };
 
 struct refname_atom {
-       enum { R_NORMAL, R_SHORT, R_STRIP } option;
-       unsigned int strip;
+       enum { R_NORMAL, R_SHORT, R_LSTRIP, R_RSTRIP } option;
+       int lstrip, rstrip;
 };
 
 /*
@@ -54,7 +54,8 @@ static struct used_atom {
                char color[COLOR_MAXLEN];
                struct align align;
                struct {
-                       enum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT } option;
+                       enum { RR_REF, RR_TRACK, RR_TRACKSHORT } option;
+                       struct refname_atom refname;
                        unsigned int nobracket : 1;
                } remote_ref;
                struct {
@@ -90,10 +91,14 @@ static void refname_atom_parser_internal(struct refname_atom *atom,
                atom->option = R_NORMAL;
        else if (!strcmp(arg, "short"))
                atom->option = R_SHORT;
-       else if (skip_prefix(arg, "strip=", &arg)) {
-               atom->option = R_STRIP;
-               if (strtoul_ui(arg, 10, &atom->strip) || atom->strip <= 0)
-                       die(_("positive value expected refname:strip=%s"), arg);
+       else if (skip_prefix(arg, "lstrip=", &arg)) {
+               atom->option = R_LSTRIP;
+               if (strtol_i(arg, 10, &atom->lstrip))
+                       die(_("Integer value expected refname:lstrip=%s"), arg);
+       } else if (skip_prefix(arg, "rstrip=", &arg)) {
+               atom->option = R_RSTRIP;
+               if (strtol_i(arg, 10, &atom->rstrip))
+                       die(_("Integer value expected refname:rstrip=%s"), arg);
        } else
                die(_("unrecognized %%(%s) argument: %s"), name, arg);
 }
@@ -104,7 +109,9 @@ static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)
        int i;
 
        if (!arg) {
-               atom->u.remote_ref.option = RR_NORMAL;
+               atom->u.remote_ref.option = RR_REF;
+               refname_atom_parser_internal(&atom->u.remote_ref.refname,
+                                            arg, atom->name);
                return;
        }
 
@@ -114,16 +121,17 @@ static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)
        for (i = 0; i < params.nr; i++) {
                const char *s = params.items[i].string;
 
-               if (!strcmp(s, "short"))
-                       atom->u.remote_ref.option = RR_SHORTEN;
-               else if (!strcmp(s, "track"))
+               if (!strcmp(s, "track"))
                        atom->u.remote_ref.option = RR_TRACK;
                else if (!strcmp(s, "trackshort"))
                        atom->u.remote_ref.option = RR_TRACKSHORT;
                else if (!strcmp(s, "nobracket"))
                        atom->u.remote_ref.nobracket = 1;
-               else
-                       die(_("unrecognized format: %%(%s)"), atom->name);
+               else {
+                       atom->u.remote_ref.option = RR_REF;
+                       refname_atom_parser_internal(&atom->u.remote_ref.refname,
+                                                    arg, atom->name);
+               }
        }
 
        string_list_clear(&params, 0);
@@ -187,6 +195,11 @@ static void objectname_atom_parser(struct used_atom *atom, const char *arg)
                die(_("unrecognized %%(objectname) argument: %s"), arg);
 }
 
+static void refname_atom_parser(struct used_atom *atom, const char *arg)
+{
+       return refname_atom_parser_internal(&atom->u.refname, arg, atom->name);
+}
+
 static align_type parse_align_position(const char *s)
 {
        if (!strcmp(s, "right"))
@@ -257,7 +270,7 @@ static struct {
        cmp_type cmp_type;
        void (*parser)(struct used_atom *atom, const char *arg);
 } valid_atom[] = {
-       { "refname" },
+       { "refname" , FIELD_STR, refname_atom_parser },
        { "objecttype" },
        { "objectsize", FIELD_ULONG },
        { "objectname", FIELD_STR, objectname_atom_parser },
@@ -287,7 +300,7 @@ static struct {
        { "contents", FIELD_STR, contents_atom_parser },
        { "upstream", FIELD_STR, remote_ref_atom_parser },
        { "push", FIELD_STR, remote_ref_atom_parser },
-       { "symref" },
+       { "symref", FIELD_STR, refname_atom_parser },
        { "flag" },
        { "HEAD" },
        { "color", FIELD_STR, color_atom_parser },
@@ -1082,35 +1095,89 @@ static inline char *copy_advance(char *dst, const char *src)
        return dst;
 }
 
-static const char *strip_ref_components(const char *refname, const char *nr_arg)
+static const char *lstrip_ref_components(const char *refname, int len)
 {
-       char *end;
-       long nr = strtol(nr_arg, &end, 10);
-       long remaining = nr;
+       long remaining = len;
        const char *start = refname;
 
-       if (nr < 1 || *end != '\0')
-               die(_(":strip= requires a positive integer argument"));
+       if (len < 0) {
+               int i;
+               const char *p = refname;
 
-       while (remaining) {
+               /* Find total no of '/' separated path-components */
+               for (i = 0; p[i]; p[i] == '/' ? i++ : *p++)
+                       ;
+               /*
+                * The number of components we need to strip is now
+                * the total minus the components to be left (Plus one
+                * because we count the number of '/', but the number
+                * of components is one more than the no of '/').
+                */
+               remaining = i + len + 1;
+       }
+
+       while (remaining > 0) {
                switch (*start++) {
                case '\0':
-                       die(_("ref '%s' does not have %ld components to :strip"),
-                           refname, nr);
+                       return "";
                case '/':
                        remaining--;
                        break;
                }
        }
+
        return start;
 }
 
+static const char *rstrip_ref_components(const char *refname, int len)
+{
+       long remaining = len;
+       char *start = xstrdup(refname);
+
+       if (len < 0) {
+               int i;
+               const char *p = refname;
+
+               /* Find total no of '/' separated path-components */
+               for (i = 0; p[i]; p[i] == '/' ? i++ : *p++)
+                       ;
+               /*
+                * The number of components we need to strip is now
+                * the total minus the components to be left (Plus one
+                * because we count the number of '/', but the number
+                * of components is one more than the no of '/').
+                */
+               remaining = i + len + 1;
+       }
+
+       while (remaining-- > 0) {
+               char *p = strrchr(start, '/');
+               if (p == NULL)
+                       return "";
+               else
+                       p[0] = '\0';
+       }
+       return start;
+}
+
+static const char *show_ref(struct refname_atom *atom, const char *refname)
+{
+       if (atom->option == R_SHORT)
+               return shorten_unambiguous_ref(refname, warn_ambiguous_refs);
+       else if (atom->option == R_LSTRIP)
+               return lstrip_ref_components(refname, atom->lstrip);
+       else if (atom->option == R_RSTRIP)
+               return rstrip_ref_components(refname, atom->rstrip);
+       else
+               return refname;
+}
+
 static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
                                    struct branch *branch, const char **s)
 {
        int num_ours, num_theirs;
-       if (atom->u.remote_ref.option == RR_SHORTEN)
-               *s = shorten_unambiguous_ref(refname, warn_ambiguous_refs);
+       if (atom->u.remote_ref.option == RR_REF)
+               *s = show_ref(&atom->u.remote_ref.refname, refname);
        else if (atom->u.remote_ref.option == RR_TRACK) {
                if (stat_tracking_info(branch, &num_ours,
                                       &num_theirs, NULL)) {
@@ -1142,8 +1209,8 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
                        *s = ">";
                else
                        *s = "<>";
-       } else /* RR_NORMAL */
-               *s = refname;
+       } else
+               die("BUG: unhandled RR_* enum");
 }
 
 char *get_head_description(void)
@@ -1177,6 +1244,21 @@ char *get_head_description(void)
        return strbuf_detach(&desc, NULL);
 }
 
+static const char *get_symref(struct used_atom *atom, struct ref_array_item *ref)
+{
+       if (!ref->symref)
+               return "";
+       else
+               return show_ref(&atom->u.refname, ref->symref);
+}
+
+static const char *get_refname(struct used_atom *atom, struct ref_array_item *ref)
+{
+       if (ref->kind & FILTER_REFS_DETACHED_HEAD)
+               return get_head_description();
+       return show_ref(&atom->u.refname, ref->refname);
+}
+
 /*
  * Parse the object referred by ref, and grab needed value.
  */
@@ -1205,7 +1287,6 @@ static void populate_value(struct ref_array_item *ref)
                struct atom_value *v = &ref->value[i];
                int deref = 0;
                const char *refname;
-               const char *formatp;
                struct branch *branch = NULL;
 
                v->handler = append_atom;
@@ -1216,12 +1297,10 @@ static void populate_value(struct ref_array_item *ref)
                        name++;
                }
 
-               if (starts_with(name, "refname")) {
-                       refname = ref->refname;
-                       if (ref->kind & FILTER_REFS_DETACHED_HEAD)
-                               refname = get_head_description();
-               } else if (starts_with(name, "symref"))
-                       refname = ref->symref ? ref->symref : "";
+               if (starts_with(name, "refname"))
+                       refname = get_refname(atom, ref);
+               else if (starts_with(name, "symref"))
+                       refname = get_symref(atom, ref);
                else if (starts_with(name, "upstream")) {
                        const char *branch_name;
                        /* only local branches may have an upstream */
@@ -1297,21 +1376,6 @@ static void populate_value(struct ref_array_item *ref)
                } else
                        continue;
 
-               formatp = strchr(name, ':');
-               if (formatp) {
-                       const char *arg;
-
-                       formatp++;
-                       if (!strcmp(formatp, "short"))
-                               refname = shorten_unambiguous_ref(refname,
-                                                     warn_ambiguous_refs);
-                       else if (skip_prefix(formatp, "strip=", &arg))
-                               refname = strip_ref_components(refname, arg);
-                       else
-                               die(_("unknown %.*s format %s"),
-                                   (int)(formatp - name), name, formatp);
-               }
-
                if (!deref)
                        v->s = refname;
                else