diff options
Diffstat (limited to 'ref-filter.c')
-rw-r--r-- | ref-filter.c | 434 |
1 files changed, 250 insertions, 184 deletions
diff --git a/ref-filter.c b/ref-filter.c index f097176ed9..d13d002270 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -16,14 +16,162 @@ typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type; +struct align { + align_type position; + unsigned int width; +}; + +/* + * An atom is a valid field atom listed below, possibly prefixed with + * a "*" to denote deref_tag(). + * + * We parse given format string and sort specifiers, and make a list + * of properties that we need to extract out of objects. ref_array_item + * structure will hold an array of values extracted that can be + * indexed with the "atom number", which is an index into this + * array. + */ +static struct used_atom { + const char *name; + cmp_type type; + union { + char color[COLOR_MAXLEN]; + struct align align; + enum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT } + remote_ref; + struct { + enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB } option; + unsigned int nlines; + } contents; + enum { O_FULL, O_SHORT } objectname; + } u; +} *used_atom; +static int used_atom_cnt, need_tagged, need_symref; +static int need_color_reset_at_eol; + +static void color_atom_parser(struct used_atom *atom, const char *color_value) +{ + if (!color_value) + die(_("expected format: %%(color:<color>)")); + if (color_parse(color_value, atom->u.color) < 0) + die(_("unrecognized color: %%(color:%s)"), color_value); +} + +static void remote_ref_atom_parser(struct used_atom *atom, const char *arg) +{ + if (!arg) + atom->u.remote_ref = RR_NORMAL; + else if (!strcmp(arg, "short")) + atom->u.remote_ref = RR_SHORTEN; + else if (!strcmp(arg, "track")) + atom->u.remote_ref = RR_TRACK; + else if (!strcmp(arg, "trackshort")) + atom->u.remote_ref = RR_TRACKSHORT; + else + die(_("unrecognized format: %%(%s)"), atom->name); +} + +static void body_atom_parser(struct used_atom *atom, const char *arg) +{ + if (arg) + die("%%(body) does not take arguments"); + atom->u.contents.option = C_BODY_DEP; +} + +static void subject_atom_parser(struct used_atom *atom, const char *arg) +{ + if (arg) + die("%%(subject) does not take arguments"); + atom->u.contents.option = C_SUB; +} + +static void contents_atom_parser(struct used_atom *atom, const char *arg) +{ + if (!arg) + atom->u.contents.option = C_BARE; + else if (!strcmp(arg, "body")) + atom->u.contents.option = C_BODY; + else if (!strcmp(arg, "signature")) + atom->u.contents.option = C_SIG; + else if (!strcmp(arg, "subject")) + atom->u.contents.option = C_SUB; + else if (skip_prefix(arg, "lines=", &arg)) { + atom->u.contents.option = C_LINES; + if (strtoul_ui(arg, 10, &atom->u.contents.nlines)) + die(_("positive value expected contents:lines=%s"), arg); + } else + die(_("unrecognized %%(contents) argument: %s"), arg); +} + +static void objectname_atom_parser(struct used_atom *atom, const char *arg) +{ + if (!arg) + atom->u.objectname = O_FULL; + else if (!strcmp(arg, "short")) + atom->u.objectname = O_SHORT; + else + die(_("unrecognized %%(objectname) argument: %s"), arg); +} + +static align_type parse_align_position(const char *s) +{ + if (!strcmp(s, "right")) + return ALIGN_RIGHT; + else if (!strcmp(s, "middle")) + return ALIGN_MIDDLE; + else if (!strcmp(s, "left")) + return ALIGN_LEFT; + return -1; +} + +static void align_atom_parser(struct used_atom *atom, const char *arg) +{ + struct align *align = &atom->u.align; + struct string_list params = STRING_LIST_INIT_DUP; + int i; + unsigned int width = ~0U; + + if (!arg) + die(_("expected format: %%(align:<width>,<position>)")); + + align->position = ALIGN_LEFT; + + string_list_split(¶ms, arg, ',', -1); + for (i = 0; i < params.nr; i++) { + const char *s = params.items[i].string; + int position; + + if (skip_prefix(s, "position=", &s)) { + position = parse_align_position(s); + if (position < 0) + die(_("unrecognized position:%s"), s); + align->position = position; + } else if (skip_prefix(s, "width=", &s)) { + if (strtoul_ui(s, 10, &width)) + die(_("unrecognized width:%s"), s); + } else if (!strtoul_ui(s, 10, &width)) + ; + else if ((position = parse_align_position(s)) >= 0) + align->position = position; + else + die(_("unrecognized %%(align) argument: %s"), s); + } + + if (width == ~0U) + die(_("positive width expected with the %%(align) atom")); + align->width = width; + string_list_clear(¶ms, 0); +} + static struct { const char *name; cmp_type cmp_type; + void (*parser)(struct used_atom *atom, const char *arg); } valid_atom[] = { { "refname" }, { "objecttype" }, { "objectsize", FIELD_ULONG }, - { "objectname" }, + { "objectname", FIELD_STR, objectname_atom_parser }, { "tree" }, { "parent" }, { "numparent", FIELD_ULONG }, @@ -44,31 +192,21 @@ static struct { { "taggerdate", FIELD_TIME }, { "creator" }, { "creatordate", FIELD_TIME }, - { "subject" }, - { "body" }, - { "contents" }, - { "upstream" }, - { "push" }, + { "subject", FIELD_STR, subject_atom_parser }, + { "body", FIELD_STR, body_atom_parser }, + { "contents", FIELD_STR, contents_atom_parser }, + { "upstream", FIELD_STR, remote_ref_atom_parser }, + { "push", FIELD_STR, remote_ref_atom_parser }, { "symref" }, { "flag" }, { "HEAD" }, - { "color" }, - { "align" }, + { "color", FIELD_STR, color_atom_parser }, + { "align", FIELD_STR, align_atom_parser }, { "end" }, }; #define REF_FORMATTING_STATE_INIT { 0, NULL } -struct align { - align_type position; - unsigned int width; -}; - -struct contents { - unsigned int lines; - struct object_id oid; -}; - struct ref_formatting_stack { struct ref_formatting_stack *prev; struct strbuf output; @@ -85,33 +223,18 @@ struct atom_value { const char *s; union { struct align align; - struct contents contents; } u; void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state); unsigned long ul; /* used for sorting when not FIELD_STR */ }; /* - * An atom is a valid field atom listed above, possibly prefixed with - * a "*" to denote deref_tag(). - * - * We parse given format string and sort specifiers, and make a list - * of properties that we need to extract out of objects. ref_array_item - * structure will hold an array of values extracted that can be - * indexed with the "atom number", which is an index into this - * array. - */ -static const char **used_atom; -static cmp_type *used_atom_type; -static int used_atom_cnt, need_tagged, need_symref; -static int need_color_reset_at_eol; - -/* * Used to parse format string and sort specifiers */ int parse_ref_filter_atom(const char *atom, const char *ep) { const char *sp; + const char *arg; int i, at; sp = atom; @@ -122,24 +245,24 @@ int parse_ref_filter_atom(const char *atom, const char *ep) /* Do we have the atom already used elsewhere? */ for (i = 0; i < used_atom_cnt; i++) { - int len = strlen(used_atom[i]); - if (len == ep - atom && !memcmp(used_atom[i], atom, len)) + 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); + /* * If the atom name has a colon, strip it and everything after * it off - it specifies the format for this entry, and * shouldn't be used for checking against the valid_atom * table. */ - const char *formatp = strchr(sp, ':'); - if (!formatp || ep < formatp) - formatp = ep; - if (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len)) + arg = memchr(sp, ':', ep - sp); + if (len == (arg ? arg : ep) - sp && + !memcmp(valid_atom[i].name, sp, len)) break; } @@ -150,12 +273,16 @@ int parse_ref_filter_atom(const char *atom, const char *ep) at = used_atom_cnt; used_atom_cnt++; REALLOC_ARRAY(used_atom, used_atom_cnt); - REALLOC_ARRAY(used_atom_type, used_atom_cnt); - used_atom[at] = xmemdupz(atom, ep - atom); - used_atom_type[at] = valid_atom[i].cmp_type; + used_atom[at].name = xmemdupz(atom, ep - atom); + used_atom[at].type = valid_atom[i].cmp_type; + if (arg) + arg = used_atom[at].name + (arg - atom) + 1; + memset(&used_atom[at].u, 0, sizeof(used_atom[at].u)); + if (valid_atom[i].parser) + valid_atom[i].parser(&used_atom[at], arg); if (*atom == '*') need_tagged = 1; - if (!strcmp(used_atom[at], "symref")) + if (!strcmp(used_atom[at].name, "symref")) need_symref = 1; return at; } @@ -258,22 +385,6 @@ static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_sta pop_stack_element(&state->stack); } -static int match_atom_name(const char *name, const char *atom_name, const char **val) -{ - const char *body; - - if (!skip_prefix(name, atom_name, &body)) - return 0; /* doesn't even begin with "atom_name" */ - if (!body[0]) { - *val = NULL; /* %(atom_name) and no customization */ - return 1; - } - if (body[0] != ':') - return 0; /* "atom_namefoo" is not "atom_name" or "atom_name:..." */ - *val = body + 1; /* "atom_name:val" */ - return 1; -} - /* * In a format string, find the next occurrence of %(atom). */ @@ -315,7 +426,7 @@ int verify_ref_format(const char *format) at = parse_ref_filter_atom(sp + 2, ep); cp = ep + 1; - if (skip_prefix(used_atom[at], "color:", &color)) + if (skip_prefix(used_atom[at].name, "color:", &color)) need_color_reset_at_eol = !!strcmp(color, "reset"); } return 0; @@ -340,15 +451,17 @@ static void *get_obj(const unsigned char *sha1, struct object **obj, unsigned lo } static int grab_objectname(const char *name, const unsigned char *sha1, - struct atom_value *v) + struct atom_value *v, struct used_atom *atom) { - if (!strcmp(name, "objectname")) { - v->s = xstrdup(sha1_to_hex(sha1)); - return 1; - } - if (!strcmp(name, "objectname:short")) { - v->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV)); - return 1; + if (starts_with(name, "objectname")) { + if (atom->u.objectname == O_SHORT) { + v->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV)); + return 1; + } else if (atom->u.objectname == O_FULL) { + v->s = xstrdup(sha1_to_hex(sha1)); + return 1; + } else + die("BUG: unknown %%(objectname) option"); } return 0; } @@ -359,7 +472,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct object int i; for (i = 0; i < used_atom_cnt; i++) { - const char *name = used_atom[i]; + const char *name = used_atom[i].name; struct atom_value *v = &val[i]; if (!!deref != (*name == '*')) continue; @@ -372,7 +485,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct object v->s = xstrfmt("%lu", sz); } else if (deref) - grab_objectname(name, obj->oid.hash, v); + grab_objectname(name, obj->oid.hash, v, &used_atom[i]); } } @@ -383,7 +496,7 @@ static void grab_tag_values(struct atom_value *val, int deref, struct object *ob struct tag *tag = (struct tag *) obj; for (i = 0; i < used_atom_cnt; i++) { - const char *name = used_atom[i]; + const char *name = used_atom[i].name; struct atom_value *v = &val[i]; if (!!deref != (*name == '*')) continue; @@ -405,7 +518,7 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object struct commit *commit = (struct commit *) obj; for (i = 0; i < used_atom_cnt; i++) { - const char *name = used_atom[i]; + const char *name = used_atom[i].name; struct atom_value *v = &val[i]; if (!!deref != (*name == '*')) continue; @@ -535,7 +648,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru const char *wholine = NULL; for (i = 0; i < used_atom_cnt; i++) { - const char *name = used_atom[i]; + const char *name = used_atom[i].name; struct atom_value *v = &val[i]; if (!!deref != (*name == '*')) continue; @@ -573,7 +686,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru if (!wholine) return; for (i = 0; i < used_atom_cnt; i++) { - const char *name = used_atom[i]; + const char *name = used_atom[i].name; struct atom_value *v = &val[i]; if (!!deref != (*name == '*')) continue; @@ -663,20 +776,16 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct obj unsigned long sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0; for (i = 0; i < used_atom_cnt; i++) { - const char *name = used_atom[i]; + struct used_atom *atom = &used_atom[i]; + const char *name = atom->name; struct atom_value *v = &val[i]; - const char *valp = NULL; if (!!deref != (*name == '*')) continue; if (deref) name++; if (strcmp(name, "subject") && strcmp(name, "body") && - strcmp(name, "contents") && - strcmp(name, "contents:subject") && - strcmp(name, "contents:body") && - strcmp(name, "contents:signature") && - !starts_with(name, "contents:lines=")) + !starts_with(name, "contents")) continue; if (!subpos) find_subpos(buf, sz, @@ -684,28 +793,23 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct obj &bodypos, &bodylen, &nonsiglen, &sigpos, &siglen); - if (!strcmp(name, "subject")) + if (atom->u.contents.option == C_SUB) v->s = copy_subject(subpos, sublen); - else if (!strcmp(name, "contents:subject")) - v->s = copy_subject(subpos, sublen); - else if (!strcmp(name, "body")) + else if (atom->u.contents.option == C_BODY_DEP) v->s = xmemdupz(bodypos, bodylen); - else if (!strcmp(name, "contents:body")) + else if (atom->u.contents.option == C_BODY) v->s = xmemdupz(bodypos, nonsiglen); - else if (!strcmp(name, "contents:signature")) + else if (atom->u.contents.option == C_SIG) v->s = xmemdupz(sigpos, siglen); - else if (!strcmp(name, "contents")) - v->s = xstrdup(subpos); - else if (skip_prefix(name, "contents:lines=", &valp)) { + else if (atom->u.contents.option == C_LINES) { struct strbuf s = STRBUF_INIT; const char *contents_end = bodylen + bodypos - siglen; - if (strtoul_ui(valp, 10, &v->u.contents.lines)) - die(_("positive value expected contents:lines=%s"), valp); /* Size is the length of the message after removing the signature */ - append_lines(&s, subpos, contents_end - subpos, v->u.contents.lines); + append_lines(&s, subpos, contents_end - subpos, atom->u.contents.nlines); v->s = strbuf_detach(&s, NULL); - } + } else if (atom->u.contents.option == C_BARE) + v->s = xstrdup(subpos); } } @@ -786,6 +890,43 @@ static const char *strip_ref_components(const char *refname, const char *nr_arg) return start; } +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 == RR_SHORTEN) + *s = shorten_unambiguous_ref(refname, warn_ambiguous_refs); + else if (atom->u.remote_ref == RR_TRACK) { + if (stat_tracking_info(branch, &num_ours, + &num_theirs, NULL)) + return; + + if (!num_ours && !num_theirs) + *s = ""; + else if (!num_ours) + *s = xstrfmt("[behind %d]", num_theirs); + else if (!num_theirs) + *s = xstrfmt("[ahead %d]", num_ours); + else + *s = xstrfmt("[ahead %d, behind %d]", + num_ours, num_theirs); + } else if (atom->u.remote_ref == RR_TRACKSHORT) { + if (stat_tracking_info(branch, &num_ours, + &num_theirs, NULL)) + return; + + if (!num_ours && !num_theirs) + *s = "="; + else if (!num_ours) + *s = "<"; + else if (!num_theirs) + *s = ">"; + else + *s = "<>"; + } else /* RR_NORMAL */ + *s = refname; +} + /* * Parse the object referred by ref, and grab needed value. */ @@ -809,12 +950,12 @@ static void populate_value(struct ref_array_item *ref) /* Fill in specials first */ for (i = 0; i < used_atom_cnt; i++) { - const char *name = used_atom[i]; + struct used_atom *atom = &used_atom[i]; + const char *name = used_atom[i].name; struct atom_value *v = &ref->value[i]; int deref = 0; const char *refname; const char *formatp; - const char *valp; struct branch *branch = NULL; v->handler = append_atom; @@ -837,8 +978,9 @@ static void populate_value(struct ref_array_item *ref) branch = branch_get(branch_name); refname = branch_get_upstream(branch, NULL); - if (!refname) - continue; + if (refname) + fill_remote_ref_details(atom, refname, branch, &v->s); + continue; } else if (starts_with(name, "push")) { const char *branch_name; if (!skip_prefix(ref->refname, "refs/heads/", @@ -849,14 +991,10 @@ static void populate_value(struct ref_array_item *ref) refname = branch_get_push(branch, NULL); if (!refname) continue; - } else if (match_atom_name(name, "color", &valp)) { - char color[COLOR_MAXLEN] = ""; - - if (!valp) - die(_("expected format: %%(color:<color>)")); - if (color_parse(valp, color) < 0) - die(_("unable to parse format")); - v->s = xstrdup(color); + fill_remote_ref_details(atom, refname, branch, &v->s); + continue; + } else if (starts_with(name, "color:")) { + v->s = atom->u.color; continue; } else if (!strcmp(name, "flag")) { char buf[256], *cp = buf; @@ -871,7 +1009,7 @@ static void populate_value(struct ref_array_item *ref) v->s = xstrdup(buf + 1); } continue; - } else if (!deref && grab_objectname(name, ref->objectname, v)) { + } else if (!deref && grab_objectname(name, ref->objectname, v, atom)) { continue; } else if (!strcmp(name, "HEAD")) { const char *head; @@ -884,43 +1022,8 @@ static void populate_value(struct ref_array_item *ref) else v->s = " "; continue; - } else if (match_atom_name(name, "align", &valp)) { - struct align *align = &v->u.align; - struct strbuf **s, **to_free; - int width = -1; - - if (!valp) - die(_("expected format: %%(align:<width>,<position>)")); - - /* - * TODO: Implement a function similar to strbuf_split_str() - * which would omit the separator from the end of each value. - */ - s = to_free = strbuf_split_str(valp, ',', 0); - - align->position = ALIGN_LEFT; - - while (*s) { - /* Strip trailing comma */ - if (s[1]) - strbuf_setlen(s[0], s[0]->len - 1); - if (!strtoul_ui(s[0]->buf, 10, (unsigned int *)&width)) - ; - else if (!strcmp(s[0]->buf, "left")) - align->position = ALIGN_LEFT; - else if (!strcmp(s[0]->buf, "right")) - align->position = ALIGN_RIGHT; - else if (!strcmp(s[0]->buf, "middle")) - align->position = ALIGN_MIDDLE; - else - die(_("improper format entered align:%s"), s[0]->buf); - s++; - } - - if (width < 0) - die(_("positive width expected with the %%(align) atom")); - align->width = width; - strbuf_list_free(to_free); + } else if (starts_with(name, "align")) { + v->u.align = atom->u.align; v->handler = align_atom_handler; continue; } else if (!strcmp(name, "end")) { @@ -931,7 +1034,6 @@ static void populate_value(struct ref_array_item *ref) formatp = strchr(name, ':'); if (formatp) { - int num_ours, num_theirs; const char *arg; formatp++; @@ -940,43 +1042,7 @@ static void populate_value(struct ref_array_item *ref) warn_ambiguous_refs); else if (skip_prefix(formatp, "strip=", &arg)) refname = strip_ref_components(refname, arg); - else if (!strcmp(formatp, "track") && - (starts_with(name, "upstream") || - starts_with(name, "push"))) { - - if (stat_tracking_info(branch, &num_ours, - &num_theirs, NULL)) - continue; - - if (!num_ours && !num_theirs) - v->s = ""; - else if (!num_ours) - v->s = xstrfmt("[behind %d]", num_theirs); - else if (!num_theirs) - v->s = xstrfmt("[ahead %d]", num_ours); - else - v->s = xstrfmt("[ahead %d, behind %d]", - num_ours, num_theirs); - continue; - } else if (!strcmp(formatp, "trackshort") && - (starts_with(name, "upstream") || - starts_with(name, "push"))) { - assert(branch); - - if (stat_tracking_info(branch, &num_ours, - &num_theirs, NULL)) - continue; - - if (!num_ours && !num_theirs) - v->s = "="; - else if (!num_ours) - v->s = "<"; - else if (!num_theirs) - v->s = ">"; - else - v->s = "<>"; - continue; - } else + else die("unknown %.*s format %s", (int)(formatp - name), name, formatp); } @@ -1471,7 +1537,7 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru { struct atom_value *va, *vb; int cmp; - cmp_type cmp_type = used_atom_type[s->atom]; + cmp_type cmp_type = used_atom[s->atom].type; get_ref_atom_value(a, s->atom, &va); get_ref_atom_value(b, s->atom, &vb); |