]> git.ipfire.org Git - thirdparty/git.git/blobdiff - ref-filter.c
ref-filter: add %(raw) atom
[thirdparty/git.git] / ref-filter.c
index 5cee6512fbafa88c5c0f1dcb6306b016538480c3..0d5eb91ed5432b50bb95baf33679944f9de9bc93 100644 (file)
@@ -144,6 +144,7 @@ enum atom_type {
        ATOM_BODY,
        ATOM_TRAILERS,
        ATOM_CONTENTS,
+       ATOM_RAW,
        ATOM_UPSTREAM,
        ATOM_PUSH,
        ATOM_SYMREF,
@@ -189,6 +190,9 @@ static struct used_atom {
                        struct process_trailer_options trailer_opts;
                        unsigned int nlines;
                } contents;
+               struct {
+                       enum { RAW_BARE, RAW_LENGTH } option;
+               } raw_data;
                struct {
                        cmp_status cmp_status;
                        const char *str;
@@ -426,6 +430,18 @@ static int contents_atom_parser(const struct ref_format *format, struct used_ato
        return 0;
 }
 
+static int raw_atom_parser(const struct ref_format *format, struct used_atom *atom,
+                               const char *arg, struct strbuf *err)
+{
+       if (!arg)
+               atom->u.raw_data.option = RAW_BARE;
+       else if (!strcmp(arg, "size"))
+               atom->u.raw_data.option = RAW_LENGTH;
+       else
+               return strbuf_addf_ret(err, -1, _("unrecognized %%(raw) argument: %s"), arg);
+       return 0;
+}
+
 static int oid_atom_parser(const struct ref_format *format, struct used_atom *atom,
                           const char *arg, struct strbuf *err)
 {
@@ -586,6 +602,7 @@ static struct {
        [ATOM_BODY] = { "body", SOURCE_OBJ, FIELD_STR, body_atom_parser },
        [ATOM_TRAILERS] = { "trailers", SOURCE_OBJ, FIELD_STR, trailers_atom_parser },
        [ATOM_CONTENTS] = { "contents", SOURCE_OBJ, FIELD_STR, contents_atom_parser },
+       [ATOM_RAW] = { "raw", SOURCE_OBJ, FIELD_STR, raw_atom_parser },
        [ATOM_UPSTREAM] = { "upstream", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser },
        [ATOM_PUSH] = { "push", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser },
        [ATOM_SYMREF] = { "symref", SOURCE_NONE, FIELD_STR, refname_atom_parser },
@@ -620,12 +637,19 @@ struct ref_formatting_state {
 
 struct atom_value {
        const char *s;
+       ssize_t s_size;
        int (*handler)(struct atom_value *atomv, struct ref_formatting_state *state,
                       struct strbuf *err);
        uintmax_t value; /* used for sorting when not FIELD_STR */
        struct used_atom *atom;
 };
 
+#define ATOM_SIZE_UNSPECIFIED (-1)
+
+#define ATOM_VALUE_INIT { \
+       .s_size = ATOM_SIZE_UNSPECIFIED \
+}
+
 /*
  * Used to parse format string and sort specifiers
  */
@@ -644,13 +668,6 @@ static int parse_ref_filter_atom(const struct ref_format *format,
                return strbuf_addf_ret(err, -1, _("malformed field name: %.*s"),
                                       (int)(ep-atom), atom);
 
-       /* Do we have the atom already used elsewhere? */
-       for (i = 0; i < used_atom_cnt; i++) {
-               int len = strlen(used_atom[i].name);
-               if (len == ep - atom && !memcmp(used_atom[i].name, atom, len))
-                       return i;
-       }
-
        /*
         * If the atom name has a colon, strip it and everything after
         * it off - it specifies the format for this entry, and
@@ -660,6 +677,13 @@ static int parse_ref_filter_atom(const struct ref_format *format,
        arg = memchr(sp, ':', ep - sp);
        atom_len = (arg ? arg : ep) - sp;
 
+       /* Do we have the atom already used elsewhere? */
+       for (i = 0; i < used_atom_cnt; i++) {
+               int len = strlen(used_atom[i].name);
+               if (len == ep - atom && !memcmp(used_atom[i].name, atom, len))
+                       return i;
+       }
+
        /* Is the atom a valid one? */
        for (i = 0; i < ARRAY_SIZE(valid_atom); i++) {
                int len = strlen(valid_atom[i].name);
@@ -709,11 +733,14 @@ static int parse_ref_filter_atom(const struct ref_format *format,
        return at;
 }
 
-static void quote_formatting(struct strbuf *s, const char *str, int quote_style)
+static void quote_formatting(struct strbuf *s, const char *str, ssize_t len, int quote_style)
 {
        switch (quote_style) {
        case QUOTE_NONE:
-               strbuf_addstr(s, str);
+               if (len < 0)
+                       strbuf_addstr(s, str);
+               else
+                       strbuf_add(s, str, len);
                break;
        case QUOTE_SHELL:
                sq_quote_buf(s, str);
@@ -740,9 +767,11 @@ static int append_atom(struct atom_value *v, struct ref_formatting_state *state,
         * encountered.
         */
        if (!state->stack->prev)
-               quote_formatting(&state->stack->output, v->s, state->quote_style);
-       else
+               quote_formatting(&state->stack->output, v->s, v->s_size, state->quote_style);
+       else if (v->s_size < 0)
                strbuf_addstr(&state->stack->output, v->s);
+       else
+               strbuf_add(&state->stack->output, v->s, v->s_size);
        return 0;
 }
 
@@ -842,21 +871,23 @@ static int if_atom_handler(struct atom_value *atomv, struct ref_formatting_state
        return 0;
 }
 
-static int is_empty(const char *s)
+static int is_empty(struct strbuf *buf)
 {
-       while (*s != '\0') {
-               if (!isspace(*s))
-                       return 0;
-               s++;
-       }
-       return 1;
-}
+       const char *cur = buf->buf;
+       const char *end = buf->buf + buf->len;
+
+       while (cur != end && (isspace(*cur)))
+               cur++;
+
+       return cur == end;
+ }
 
 static int then_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
                             struct strbuf *err)
 {
        struct ref_formatting_stack *cur = state->stack;
        struct if_then_else *if_then_else = NULL;
+       size_t str_len = 0;
 
        if (cur->at_end == if_then_else_handler)
                if_then_else = (struct if_then_else *)cur->at_end_data;
@@ -867,18 +898,22 @@ static int then_atom_handler(struct atom_value *atomv, struct ref_formatting_sta
        if (if_then_else->else_atom_seen)
                return strbuf_addf_ret(err, -1, _("format: %%(then) atom used after %%(else)"));
        if_then_else->then_atom_seen = 1;
+       if (if_then_else->str)
+               str_len = strlen(if_then_else->str);
        /*
         * If the 'equals' or 'notequals' attribute is used then
         * perform the required comparison. If not, only non-empty
         * strings satisfy the 'if' condition.
         */
        if (if_then_else->cmp_status == COMPARE_EQUAL) {
-               if (!strcmp(if_then_else->str, cur->output.buf))
+               if (str_len == cur->output.len &&
+                   !memcmp(if_then_else->str, cur->output.buf, cur->output.len))
                        if_then_else->condition_satisfied = 1;
        } else if (if_then_else->cmp_status == COMPARE_UNEQUAL) {
-               if (strcmp(if_then_else->str, cur->output.buf))
+               if (str_len != cur->output.len ||
+                   memcmp(if_then_else->str, cur->output.buf, cur->output.len))
                        if_then_else->condition_satisfied = 1;
-       } else if (cur->output.len && !is_empty(cur->output.buf))
+       } else if (cur->output.len && !is_empty(&cur->output))
                if_then_else->condition_satisfied = 1;
        strbuf_reset(&cur->output);
        return 0;
@@ -924,7 +959,7 @@ static int end_atom_handler(struct atom_value *atomv, struct ref_formatting_stat
         * only on the topmost supporting atom.
         */
        if (!current->prev->prev) {
-               quote_formatting(&s, current->output.buf, state->quote_style);
+               quote_formatting(&s, current->output.buf, current->output.len, state->quote_style);
                strbuf_swap(&current->output, &s);
        }
        strbuf_release(&s);
@@ -974,6 +1009,10 @@ int verify_ref_format(struct ref_format *format)
                at = parse_ref_filter_atom(format, sp + 2, ep, &err);
                if (at < 0)
                        die("%s", err.buf);
+               if (format->quote_style && used_atom[at].atom_type == ATOM_RAW &&
+                   used_atom[at].u.raw_data.option == RAW_BARE)
+                       die(_("--format=%.*s cannot be used with"
+                             "--python, --shell, --tcl, --perl"), (int)(ep - sp - 2), sp + 2);
                cp = ep + 1;
 
                if (skip_prefix(used_atom[at].name, "color:", &color))
@@ -1367,12 +1406,25 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct exp
                struct used_atom *atom = &used_atom[i];
                const char *name = atom->name;
                struct atom_value *v = &val[i];
+               enum atom_type atom_type = atom->atom_type;
 
                if (!!deref != (*name == '*'))
                        continue;
                if (deref)
                        name++;
 
+               if (atom_type == ATOM_RAW) {
+                       unsigned long buf_size = data->size;
+
+                       if (atom->u.raw_data.option == RAW_BARE) {
+                               v->s = xmemdupz(buf, buf_size);
+                               v->s_size = buf_size;
+                       } else if (atom->u.raw_data.option == RAW_LENGTH) {
+                               v->s = xstrfmt("%"PRIuMAX, (uintmax_t)buf_size);
+                       }
+                       continue;
+               }
+
                if ((data->type != OBJ_TAG &&
                     data->type != OBJ_COMMIT) ||
                    (strcmp(name, "body") &&
@@ -1460,9 +1512,11 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, s
                break;
        case OBJ_TREE:
                /* grab_tree_values(val, deref, obj, buf, sz); */
+               grab_sub_body_contents(val, deref, data);
                break;
        case OBJ_BLOB:
                /* grab_blob_values(val, deref, obj, buf, sz); */
+               grab_sub_body_contents(val, deref, data);
                break;
        default:
                die("Eh?  Object of type %d?", obj->type);
@@ -1766,6 +1820,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
                const char *refname;
                struct branch *branch = NULL;
 
+               v->s_size = ATOM_SIZE_UNSPECIFIED;
                v->handler = append_atom;
                v->atom = atom;
 
@@ -2369,6 +2424,19 @@ static int compare_detached_head(struct ref_array_item *a, struct ref_array_item
        return 0;
 }
 
+static int memcasecmp(const void *vs1, const void *vs2, size_t n)
+{
+       const char *s1 = vs1, *s2 = vs2;
+       const char *end = s1 + n;
+
+       for (; s1 < end; s1++, s2++) {
+               int diff = tolower(*s1) - tolower(*s2);
+               if (diff)
+                       return diff;
+       }
+       return 0;
+}
+
 static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, struct ref_array_item *b)
 {
        struct atom_value *va, *vb;
@@ -2389,10 +2457,29 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru
        } else if (s->sort_flags & REF_SORTING_VERSION) {
                cmp = versioncmp(va->s, vb->s);
        } else if (cmp_type == FIELD_STR) {
-               int (*cmp_fn)(const char *, const char *);
-               cmp_fn = s->sort_flags & REF_SORTING_ICASE
-                       ? strcasecmp : strcmp;
-               cmp = cmp_fn(va->s, vb->s);
+               if (va->s_size < 0 && vb->s_size < 0) {
+                       int (*cmp_fn)(const char *, const char *);
+                       cmp_fn = s->sort_flags & REF_SORTING_ICASE
+                               ? strcasecmp : strcmp;
+                       cmp = cmp_fn(va->s, vb->s);
+               } else {
+                       size_t a_size = va->s_size < 0 ?
+                                       strlen(va->s) : va->s_size;
+                       size_t b_size = vb->s_size < 0 ?
+                                       strlen(vb->s) : vb->s_size;
+                       int (*cmp_fn)(const void *, const void *, size_t);
+                       cmp_fn = s->sort_flags & REF_SORTING_ICASE
+                               ? memcasecmp : memcmp;
+
+                       cmp = cmp_fn(va->s, vb->s, b_size > a_size ?
+                                    a_size : b_size);
+                       if (!cmp) {
+                               if (a_size > b_size)
+                                       cmp = 1;
+                               else if (a_size < b_size)
+                                       cmp = -1;
+                       }
+               }
        } else {
                if (va->value < vb->value)
                        cmp = -1;
@@ -2491,7 +2578,7 @@ int format_ref_array_item(struct ref_array_item *info,
                append_literal(cp, sp, &state);
        }
        if (format->need_color_reset_at_eol) {
-               struct atom_value resetv;
+               struct atom_value resetv = ATOM_VALUE_INIT;
                resetv.s = GIT_COLOR_RESET;
                if (append_atom(&resetv, &state, error_buf)) {
                        pop_stack_element(&state.stack);