| File: | ref-filter.c |
| Location: | line 1291, column 3 |
| Description: | Value stored to 'flags' is never read |
| 1 | #include "builtin.h" |
| 2 | #include "cache.h" |
| 3 | #include "parse-options.h" |
| 4 | #include "refs.h" |
| 5 | #include "wildmatch.h" |
| 6 | #include "commit.h" |
| 7 | #include "remote.h" |
| 8 | #include "color.h" |
| 9 | #include "tag.h" |
| 10 | #include "quote.h" |
| 11 | #include "ref-filter.h" |
| 12 | #include "revision.h" |
| 13 | #include "utf8.h" |
| 14 | #include "git-compat-util.h" |
| 15 | #include "version.h" |
| 16 | #include "trailer.h" |
| 17 | |
| 18 | typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type; |
| 19 | |
| 20 | struct align { |
| 21 | align_type position; |
| 22 | unsigned int width; |
| 23 | }; |
| 24 | |
| 25 | /* |
| 26 | * An atom is a valid field atom listed below, possibly prefixed with |
| 27 | * a "*" to denote deref_tag(). |
| 28 | * |
| 29 | * We parse given format string and sort specifiers, and make a list |
| 30 | * of properties that we need to extract out of objects. ref_array_item |
| 31 | * structure will hold an array of values extracted that can be |
| 32 | * indexed with the "atom number", which is an index into this |
| 33 | * array. |
| 34 | */ |
| 35 | static struct used_atom { |
| 36 | const char *name; |
| 37 | cmp_type type; |
| 38 | union { |
| 39 | char color[COLOR_MAXLEN75]; |
| 40 | struct align align; |
| 41 | enum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT } |
| 42 | remote_ref; |
| 43 | struct { |
| 44 | enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB, C_TRAILERS } option; |
| 45 | unsigned int nlines; |
| 46 | } contents; |
| 47 | enum { O_FULL, O_SHORT } objectname; |
| 48 | } u; |
| 49 | } *used_atom; |
| 50 | static int used_atom_cnt, need_tagged, need_symref; |
| 51 | static int need_color_reset_at_eol; |
| 52 | |
| 53 | static void color_atom_parser(struct used_atom *atom, const char *color_value) |
| 54 | { |
| 55 | if (!color_value) |
| 56 | die(_("expected format: %%(color:<color>)")); |
| 57 | if (color_parse(color_value, atom->u.color) < 0) |
| 58 | die(_("unrecognized color: %%(color:%s)"), color_value); |
| 59 | } |
| 60 | |
| 61 | static void remote_ref_atom_parser(struct used_atom *atom, const char *arg) |
| 62 | { |
| 63 | if (!arg) |
| 64 | atom->u.remote_ref = RR_NORMAL; |
| 65 | else if (!strcmp(arg, "short")) |
| 66 | atom->u.remote_ref = RR_SHORTEN; |
| 67 | else if (!strcmp(arg, "track")) |
| 68 | atom->u.remote_ref = RR_TRACK; |
| 69 | else if (!strcmp(arg, "trackshort")) |
| 70 | atom->u.remote_ref = RR_TRACKSHORT; |
| 71 | else |
| 72 | die(_("unrecognized format: %%(%s)"), atom->name); |
| 73 | } |
| 74 | |
| 75 | static void body_atom_parser(struct used_atom *atom, const char *arg) |
| 76 | { |
| 77 | if (arg) |
| 78 | die(_("%%(body) does not take arguments")); |
| 79 | atom->u.contents.option = C_BODY_DEP; |
| 80 | } |
| 81 | |
| 82 | static void subject_atom_parser(struct used_atom *atom, const char *arg) |
| 83 | { |
| 84 | if (arg) |
| 85 | die(_("%%(subject) does not take arguments")); |
| 86 | atom->u.contents.option = C_SUB; |
| 87 | } |
| 88 | |
| 89 | static void trailers_atom_parser(struct used_atom *atom, const char *arg) |
| 90 | { |
| 91 | if (arg) |
| 92 | die(_("%%(trailers) does not take arguments")); |
| 93 | atom->u.contents.option = C_TRAILERS; |
| 94 | } |
| 95 | |
| 96 | static void contents_atom_parser(struct used_atom *atom, const char *arg) |
| 97 | { |
| 98 | if (!arg) |
| 99 | atom->u.contents.option = C_BARE; |
| 100 | else if (!strcmp(arg, "body")) |
| 101 | atom->u.contents.option = C_BODY; |
| 102 | else if (!strcmp(arg, "signature")) |
| 103 | atom->u.contents.option = C_SIG; |
| 104 | else if (!strcmp(arg, "subject")) |
| 105 | atom->u.contents.option = C_SUB; |
| 106 | else if (!strcmp(arg, "trailers")) |
| 107 | atom->u.contents.option = C_TRAILERS; |
| 108 | else if (skip_prefix(arg, "lines=", &arg)) { |
| 109 | atom->u.contents.option = C_LINES; |
| 110 | if (strtoul_ui(arg, 10, &atom->u.contents.nlines)) |
| 111 | die(_("positive value expected contents:lines=%s"), arg); |
| 112 | } else |
| 113 | die(_("unrecognized %%(contents) argument: %s"), arg); |
| 114 | } |
| 115 | |
| 116 | static void objectname_atom_parser(struct used_atom *atom, const char *arg) |
| 117 | { |
| 118 | if (!arg) |
| 119 | atom->u.objectname = O_FULL; |
| 120 | else if (!strcmp(arg, "short")) |
| 121 | atom->u.objectname = O_SHORT; |
| 122 | else |
| 123 | die(_("unrecognized %%(objectname) argument: %s"), arg); |
| 124 | } |
| 125 | |
| 126 | static align_type parse_align_position(const char *s) |
| 127 | { |
| 128 | if (!strcmp(s, "right")) |
| 129 | return ALIGN_RIGHT; |
| 130 | else if (!strcmp(s, "middle")) |
| 131 | return ALIGN_MIDDLE; |
| 132 | else if (!strcmp(s, "left")) |
| 133 | return ALIGN_LEFT; |
| 134 | return -1; |
| 135 | } |
| 136 | |
| 137 | static void align_atom_parser(struct used_atom *atom, const char *arg) |
| 138 | { |
| 139 | struct align *align = &atom->u.align; |
| 140 | struct string_list params = STRING_LIST_INIT_DUP{ ((void*)0), 0, 0, 1, ((void*)0) }; |
| 141 | int i; |
| 142 | unsigned int width = ~0U; |
| 143 | |
| 144 | if (!arg) |
| 145 | die(_("expected format: %%(align:<width>,<position>)")); |
| 146 | |
| 147 | align->position = ALIGN_LEFT; |
| 148 | |
| 149 | string_list_split(¶ms, arg, ',', -1); |
| 150 | for (i = 0; i < params.nr; i++) { |
| 151 | const char *s = params.items[i].string; |
| 152 | int position; |
| 153 | |
| 154 | if (skip_prefix(s, "position=", &s)) { |
| 155 | position = parse_align_position(s); |
| 156 | if (position < 0) |
| 157 | die(_("unrecognized position:%s"), s); |
| 158 | align->position = position; |
| 159 | } else if (skip_prefix(s, "width=", &s)) { |
| 160 | if (strtoul_ui(s, 10, &width)) |
| 161 | die(_("unrecognized width:%s"), s); |
| 162 | } else if (!strtoul_ui(s, 10, &width)) |
| 163 | ; |
| 164 | else if ((position = parse_align_position(s)) >= 0) |
| 165 | align->position = position; |
| 166 | else |
| 167 | die(_("unrecognized %%(align) argument: %s"), s); |
| 168 | } |
| 169 | |
| 170 | if (width == ~0U) |
| 171 | die(_("positive width expected with the %%(align) atom")); |
| 172 | align->width = width; |
| 173 | string_list_clear(¶ms, 0); |
| 174 | } |
| 175 | |
| 176 | static struct { |
| 177 | const char *name; |
| 178 | cmp_type cmp_type; |
| 179 | void (*parser)(struct used_atom *atom, const char *arg); |
| 180 | } valid_atom[] = { |
| 181 | { "refname" }, |
| 182 | { "objecttype" }, |
| 183 | { "objectsize", FIELD_ULONG }, |
| 184 | { "objectname", FIELD_STR, objectname_atom_parser }, |
| 185 | { "tree" }, |
| 186 | { "parent" }, |
| 187 | { "numparent", FIELD_ULONG }, |
| 188 | { "object" }, |
| 189 | { "type" }, |
| 190 | { "tag" }, |
| 191 | { "author" }, |
| 192 | { "authorname" }, |
| 193 | { "authoremail" }, |
| 194 | { "authordate", FIELD_TIME }, |
| 195 | { "committer" }, |
| 196 | { "committername" }, |
| 197 | { "committeremail" }, |
| 198 | { "committerdate", FIELD_TIME }, |
| 199 | { "tagger" }, |
| 200 | { "taggername" }, |
| 201 | { "taggeremail" }, |
| 202 | { "taggerdate", FIELD_TIME }, |
| 203 | { "creator" }, |
| 204 | { "creatordate", FIELD_TIME }, |
| 205 | { "subject", FIELD_STR, subject_atom_parser }, |
| 206 | { "body", FIELD_STR, body_atom_parser }, |
| 207 | { "trailers", FIELD_STR, trailers_atom_parser }, |
| 208 | { "contents", FIELD_STR, contents_atom_parser }, |
| 209 | { "upstream", FIELD_STR, remote_ref_atom_parser }, |
| 210 | { "push", FIELD_STR, remote_ref_atom_parser }, |
| 211 | { "symref" }, |
| 212 | { "flag" }, |
| 213 | { "HEAD" }, |
| 214 | { "color", FIELD_STR, color_atom_parser }, |
| 215 | { "align", FIELD_STR, align_atom_parser }, |
| 216 | { "end" }, |
| 217 | }; |
| 218 | |
| 219 | #define REF_FORMATTING_STATE_INIT{ 0, ((void*)0) } { 0, NULL((void*)0) } |
| 220 | |
| 221 | struct ref_formatting_stack { |
| 222 | struct ref_formatting_stack *prev; |
| 223 | struct strbuf output; |
| 224 | void (*at_end)(struct ref_formatting_stack *stack); |
| 225 | void *at_end_data; |
| 226 | }; |
| 227 | |
| 228 | struct ref_formatting_state { |
| 229 | int quote_style; |
| 230 | struct ref_formatting_stack *stack; |
| 231 | }; |
| 232 | |
| 233 | struct atom_value { |
| 234 | const char *s; |
| 235 | union { |
| 236 | struct align align; |
| 237 | } u; |
| 238 | void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state); |
| 239 | unsigned long ul; /* used for sorting when not FIELD_STR */ |
| 240 | }; |
| 241 | |
| 242 | /* |
| 243 | * Used to parse format string and sort specifiers |
| 244 | */ |
| 245 | int parse_ref_filter_atom(const char *atom, const char *ep) |
| 246 | { |
| 247 | const char *sp; |
| 248 | const char *arg; |
| 249 | int i, at, atom_len; |
| 250 | |
| 251 | sp = atom; |
| 252 | if (*sp == '*' && sp < ep) |
| 253 | sp++; /* deref */ |
| 254 | if (ep <= sp) |
| 255 | die(_("malformed field name: %.*s"), (int)(ep-atom), atom); |
| 256 | |
| 257 | /* Do we have the atom already used elsewhere? */ |
| 258 | for (i = 0; i < used_atom_cnt; i++) { |
| 259 | int len = strlen(used_atom[i].name); |
| 260 | if (len == ep - atom && !memcmp(used_atom[i].name, atom, len)) |
| 261 | return i; |
| 262 | } |
| 263 | |
| 264 | /* |
| 265 | * If the atom name has a colon, strip it and everything after |
| 266 | * it off - it specifies the format for this entry, and |
| 267 | * shouldn't be used for checking against the valid_atom |
| 268 | * table. |
| 269 | */ |
| 270 | arg = memchr(sp, ':', ep - sp); |
| 271 | atom_len = (arg ? arg : ep) - sp; |
| 272 | |
| 273 | /* Is the atom a valid one? */ |
| 274 | for (i = 0; i < ARRAY_SIZE(valid_atom)(sizeof(valid_atom) / sizeof((valid_atom)[0]) + (sizeof(char [ 1 - 2*!(!__builtin_types_compatible_p(__typeof__(valid_atom), __typeof__(&(valid_atom)[0])))]) - 1)); i++) { |
| 275 | int len = strlen(valid_atom[i].name); |
| 276 | if (len == atom_len && !memcmp(valid_atom[i].name, sp, len)) |
| 277 | break; |
| 278 | } |
| 279 | |
| 280 | if (ARRAY_SIZE(valid_atom)(sizeof(valid_atom) / sizeof((valid_atom)[0]) + (sizeof(char [ 1 - 2*!(!__builtin_types_compatible_p(__typeof__(valid_atom), __typeof__(&(valid_atom)[0])))]) - 1)) <= i) |
| 281 | die(_("unknown field name: %.*s"), (int)(ep-atom), atom); |
| 282 | |
| 283 | /* Add it in, including the deref prefix */ |
| 284 | at = used_atom_cnt; |
| 285 | used_atom_cnt++; |
| 286 | REALLOC_ARRAY(used_atom, used_atom_cnt)(used_atom) = xrealloc((used_atom), st_mult(sizeof(*(used_atom )), (used_atom_cnt))); |
| 287 | used_atom[at].name = xmemdupz(atom, ep - atom); |
| 288 | used_atom[at].type = valid_atom[i].cmp_type; |
| 289 | if (arg) |
| 290 | arg = used_atom[at].name + (arg - atom) + 1; |
| 291 | memset(&used_atom[at].u, 0, sizeof(used_atom[at].u))__builtin___memset_chk (&used_atom[at].u, 0, sizeof(used_atom [at].u), __builtin_object_size (&used_atom[at].u, 0)); |
| 292 | if (valid_atom[i].parser) |
| 293 | valid_atom[i].parser(&used_atom[at], arg); |
| 294 | if (*atom == '*') |
| 295 | need_tagged = 1; |
| 296 | if (!strcmp(used_atom[at].name, "symref")) |
| 297 | need_symref = 1; |
| 298 | return at; |
| 299 | } |
| 300 | |
| 301 | static void quote_formatting(struct strbuf *s, const char *str, int quote_style) |
| 302 | { |
| 303 | switch (quote_style) { |
| 304 | case QUOTE_NONE0: |
| 305 | strbuf_addstr(s, str); |
| 306 | break; |
| 307 | case QUOTE_SHELL1: |
| 308 | sq_quote_buf(s, str); |
| 309 | break; |
| 310 | case QUOTE_PERL2: |
| 311 | perl_quote_buf(s, str); |
| 312 | break; |
| 313 | case QUOTE_PYTHON4: |
| 314 | python_quote_buf(s, str); |
| 315 | break; |
| 316 | case QUOTE_TCL8: |
| 317 | tcl_quote_buf(s, str); |
| 318 | break; |
| 319 | } |
| 320 | } |
| 321 | |
| 322 | static void append_atom(struct atom_value *v, struct ref_formatting_state *state) |
| 323 | { |
| 324 | /* |
| 325 | * Quote formatting is only done when the stack has a single |
| 326 | * element. Otherwise quote formatting is done on the |
| 327 | * element's entire output strbuf when the %(end) atom is |
| 328 | * encountered. |
| 329 | */ |
| 330 | if (!state->stack->prev) |
| 331 | quote_formatting(&state->stack->output, v->s, state->quote_style); |
| 332 | else |
| 333 | strbuf_addstr(&state->stack->output, v->s); |
| 334 | } |
| 335 | |
| 336 | static void push_stack_element(struct ref_formatting_stack **stack) |
| 337 | { |
| 338 | struct ref_formatting_stack *s = xcalloc(1, sizeof(struct ref_formatting_stack)); |
| 339 | |
| 340 | strbuf_init(&s->output, 0); |
| 341 | s->prev = *stack; |
| 342 | *stack = s; |
| 343 | } |
| 344 | |
| 345 | static void pop_stack_element(struct ref_formatting_stack **stack) |
| 346 | { |
| 347 | struct ref_formatting_stack *current = *stack; |
| 348 | struct ref_formatting_stack *prev = current->prev; |
| 349 | |
| 350 | if (prev) |
| 351 | strbuf_addbuf(&prev->output, ¤t->output); |
| 352 | strbuf_release(¤t->output); |
| 353 | free(current); |
| 354 | *stack = prev; |
| 355 | } |
| 356 | |
| 357 | static void end_align_handler(struct ref_formatting_stack *stack) |
| 358 | { |
| 359 | struct align *align = (struct align *)stack->at_end_data; |
| 360 | struct strbuf s = STRBUF_INIT{ 0, 0, strbuf_slopbuf }; |
| 361 | |
| 362 | strbuf_utf8_align(&s, align->position, align->width, stack->output.buf); |
| 363 | strbuf_swap(&stack->output, &s); |
| 364 | strbuf_release(&s); |
| 365 | } |
| 366 | |
| 367 | static void align_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state) |
| 368 | { |
| 369 | struct ref_formatting_stack *new; |
| 370 | |
| 371 | push_stack_element(&state->stack); |
| 372 | new = state->stack; |
| 373 | new->at_end = end_align_handler; |
| 374 | new->at_end_data = &atomv->u.align; |
| 375 | } |
| 376 | |
| 377 | static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state) |
| 378 | { |
| 379 | struct ref_formatting_stack *current = state->stack; |
| 380 | struct strbuf s = STRBUF_INIT{ 0, 0, strbuf_slopbuf }; |
| 381 | |
| 382 | if (!current->at_end) |
| 383 | die(_("format: %%(end) atom used without corresponding atom")); |
| 384 | current->at_end(current); |
| 385 | |
| 386 | /* |
| 387 | * Perform quote formatting when the stack element is that of |
| 388 | * a supporting atom. If nested then perform quote formatting |
| 389 | * only on the topmost supporting atom. |
| 390 | */ |
| 391 | if (!state->stack->prev->prev) { |
| 392 | quote_formatting(&s, current->output.buf, state->quote_style); |
| 393 | strbuf_swap(¤t->output, &s); |
| 394 | } |
| 395 | strbuf_release(&s); |
| 396 | pop_stack_element(&state->stack); |
| 397 | } |
| 398 | |
| 399 | /* |
| 400 | * In a format string, find the next occurrence of %(atom). |
| 401 | */ |
| 402 | static const char *find_next(const char *cp) |
| 403 | { |
| 404 | while (*cp) { |
| 405 | if (*cp == '%') { |
| 406 | /* |
| 407 | * %( is the start of an atom; |
| 408 | * %% is a quoted per-cent. |
| 409 | */ |
| 410 | if (cp[1] == '(') |
| 411 | return cp; |
| 412 | else if (cp[1] == '%') |
| 413 | cp++; /* skip over two % */ |
| 414 | /* otherwise this is a singleton, literal % */ |
| 415 | } |
| 416 | cp++; |
| 417 | } |
| 418 | return NULL((void*)0); |
| 419 | } |
| 420 | |
| 421 | /* |
| 422 | * Make sure the format string is well formed, and parse out |
| 423 | * the used atoms. |
| 424 | */ |
| 425 | int verify_ref_format(const char *format) |
| 426 | { |
| 427 | const char *cp, *sp; |
| 428 | |
| 429 | need_color_reset_at_eol = 0; |
| 430 | for (cp = format; *cp && (sp = find_next(cp)); ) { |
| 431 | const char *color, *ep = strchr(sp, ')'); |
| 432 | int at; |
| 433 | |
| 434 | if (!ep) |
| 435 | return error(_("malformed format string %s"), sp)(error(_("malformed format string %s"), sp), const_error()); |
| 436 | /* sp points at "%(" and ep points at the closing ")" */ |
| 437 | at = parse_ref_filter_atom(sp + 2, ep); |
| 438 | cp = ep + 1; |
| 439 | |
| 440 | if (skip_prefix(used_atom[at].name, "color:", &color)) |
| 441 | need_color_reset_at_eol = !!strcmp(color, "reset"); |
| 442 | } |
| 443 | return 0; |
| 444 | } |
| 445 | |
| 446 | /* |
| 447 | * Given an object name, read the object data and size, and return a |
| 448 | * "struct object". If the object data we are returning is also borrowed |
| 449 | * by the "struct object" representation, set *eaten as well---it is a |
| 450 | * signal from parse_object_buffer to us not to free the buffer. |
| 451 | */ |
| 452 | static void *get_obj(const unsigned char *sha1, struct object **obj, unsigned long *sz, int *eaten) |
| 453 | { |
| 454 | enum object_type type; |
| 455 | void *buf = read_sha1_file(sha1, &type, sz); |
| 456 | |
| 457 | if (buf) |
| 458 | *obj = parse_object_buffer(sha1, type, *sz, buf, eaten); |
| 459 | else |
| 460 | *obj = NULL((void*)0); |
| 461 | return buf; |
| 462 | } |
| 463 | |
| 464 | static int grab_objectname(const char *name, const unsigned char *sha1, |
| 465 | struct atom_value *v, struct used_atom *atom) |
| 466 | { |
| 467 | if (starts_with(name, "objectname")) { |
| 468 | if (atom->u.objectname == O_SHORT) { |
| 469 | v->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREVdefault_abbrev)); |
| 470 | return 1; |
| 471 | } else if (atom->u.objectname == O_FULL) { |
| 472 | v->s = xstrdup(sha1_to_hex(sha1)); |
| 473 | return 1; |
| 474 | } else |
| 475 | die("BUG: unknown %%(objectname) option"); |
| 476 | } |
| 477 | return 0; |
| 478 | } |
| 479 | |
| 480 | /* See grab_values */ |
| 481 | static void grab_common_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz) |
| 482 | { |
| 483 | int i; |
| 484 | |
| 485 | for (i = 0; i < used_atom_cnt; i++) { |
| 486 | const char *name = used_atom[i].name; |
| 487 | struct atom_value *v = &val[i]; |
| 488 | if (!!deref != (*name == '*')) |
| 489 | continue; |
| 490 | if (deref) |
| 491 | name++; |
| 492 | if (!strcmp(name, "objecttype")) |
| 493 | v->s = typename(obj->type); |
| 494 | else if (!strcmp(name, "objectsize")) { |
| 495 | v->ul = sz; |
| 496 | v->s = xstrfmt("%lu", sz); |
| 497 | } |
| 498 | else if (deref) |
| 499 | grab_objectname(name, obj->oid.hash, v, &used_atom[i]); |
| 500 | } |
| 501 | } |
| 502 | |
| 503 | /* See grab_values */ |
| 504 | static void grab_tag_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz) |
| 505 | { |
| 506 | int i; |
| 507 | struct tag *tag = (struct tag *) obj; |
| 508 | |
| 509 | for (i = 0; i < used_atom_cnt; i++) { |
| 510 | const char *name = used_atom[i].name; |
| 511 | struct atom_value *v = &val[i]; |
| 512 | if (!!deref != (*name == '*')) |
| 513 | continue; |
| 514 | if (deref) |
| 515 | name++; |
| 516 | if (!strcmp(name, "tag")) |
| 517 | v->s = tag->tag; |
| 518 | else if (!strcmp(name, "type") && tag->tagged) |
| 519 | v->s = typename(tag->tagged->type); |
| 520 | else if (!strcmp(name, "object") && tag->tagged) |
| 521 | v->s = xstrdup(oid_to_hex(&tag->tagged->oid)); |
| 522 | } |
| 523 | } |
| 524 | |
| 525 | /* See grab_values */ |
| 526 | static void grab_commit_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz) |
| 527 | { |
| 528 | int i; |
| 529 | struct commit *commit = (struct commit *) obj; |
| 530 | |
| 531 | for (i = 0; i < used_atom_cnt; i++) { |
| 532 | const char *name = used_atom[i].name; |
| 533 | struct atom_value *v = &val[i]; |
| 534 | if (!!deref != (*name == '*')) |
| 535 | continue; |
| 536 | if (deref) |
| 537 | name++; |
| 538 | if (!strcmp(name, "tree")) { |
| 539 | v->s = xstrdup(oid_to_hex(&commit->tree->object.oid)); |
| 540 | } |
| 541 | else if (!strcmp(name, "numparent")) { |
| 542 | v->ul = commit_list_count(commit->parents); |
| 543 | v->s = xstrfmt("%lu", v->ul); |
| 544 | } |
| 545 | else if (!strcmp(name, "parent")) { |
| 546 | struct commit_list *parents; |
| 547 | struct strbuf s = STRBUF_INIT{ 0, 0, strbuf_slopbuf }; |
| 548 | for (parents = commit->parents; parents; parents = parents->next) { |
| 549 | struct commit *parent = parents->item; |
| 550 | if (parents != commit->parents) |
| 551 | strbuf_addch(&s, ' '); |
| 552 | strbuf_addstr(&s, oid_to_hex(&parent->object.oid)); |
| 553 | } |
| 554 | v->s = strbuf_detach(&s, NULL((void*)0)); |
| 555 | } |
| 556 | } |
| 557 | } |
| 558 | |
| 559 | static const char *find_wholine(const char *who, int wholen, const char *buf, unsigned long sz) |
| 560 | { |
| 561 | const char *eol; |
| 562 | while (*buf) { |
| 563 | if (!strncmp(buf, who, wholen) && |
| 564 | buf[wholen] == ' ') |
| 565 | return buf + wholen + 1; |
| 566 | eol = strchr(buf, '\n'); |
| 567 | if (!eol) |
| 568 | return ""; |
| 569 | eol++; |
| 570 | if (*eol == '\n') |
| 571 | return ""; /* end of header */ |
| 572 | buf = eol; |
| 573 | } |
| 574 | return ""; |
| 575 | } |
| 576 | |
| 577 | static const char *copy_line(const char *buf) |
| 578 | { |
| 579 | const char *eol = strchrnulgitstrchrnul(buf, '\n'); |
| 580 | return xmemdupz(buf, eol - buf); |
| 581 | } |
| 582 | |
| 583 | static const char *copy_name(const char *buf) |
| 584 | { |
| 585 | const char *cp; |
| 586 | for (cp = buf; *cp && *cp != '\n'; cp++) { |
| 587 | if (!strncmp(cp, " <", 2)) |
| 588 | return xmemdupz(buf, cp - buf); |
| 589 | } |
| 590 | return ""; |
| 591 | } |
| 592 | |
| 593 | static const char *copy_email(const char *buf) |
| 594 | { |
| 595 | const char *email = strchr(buf, '<'); |
| 596 | const char *eoemail; |
| 597 | if (!email) |
| 598 | return ""; |
| 599 | eoemail = strchr(email, '>'); |
| 600 | if (!eoemail) |
| 601 | return ""; |
| 602 | return xmemdupz(email, eoemail + 1 - email); |
| 603 | } |
| 604 | |
| 605 | static char *copy_subject(const char *buf, unsigned long len) |
| 606 | { |
| 607 | char *r = xmemdupz(buf, len); |
| 608 | int i; |
| 609 | |
| 610 | for (i = 0; i < len; i++) |
| 611 | if (r[i] == '\n') |
| 612 | r[i] = ' '; |
| 613 | |
| 614 | return r; |
| 615 | } |
| 616 | |
| 617 | static void grab_date(const char *buf, struct atom_value *v, const char *atomname) |
| 618 | { |
| 619 | const char *eoemail = strstr(buf, "> "); |
| 620 | char *zone; |
| 621 | unsigned long timestamp; |
| 622 | long tz; |
| 623 | struct date_mode date_mode = { DATE_NORMAL }; |
| 624 | const char *formatp; |
| 625 | |
| 626 | /* |
| 627 | * We got here because atomname ends in "date" or "date<something>"; |
| 628 | * it's not possible that <something> is not ":<format>" because |
| 629 | * parse_ref_filter_atom() wouldn't have allowed it, so we can assume that no |
| 630 | * ":" means no format is specified, and use the default. |
| 631 | */ |
| 632 | formatp = strchr(atomname, ':'); |
| 633 | if (formatp != NULL((void*)0)) { |
| 634 | formatp++; |
| 635 | parse_date_format(formatp, &date_mode); |
| 636 | } |
| 637 | |
| 638 | if (!eoemail) |
| 639 | goto bad; |
| 640 | timestamp = strtoul(eoemail + 2, &zone, 10); |
| 641 | if (timestamp == ULONG_MAX(9223372036854775807L *2UL+1UL)) |
| 642 | goto bad; |
| 643 | tz = strtol(zone, NULL((void*)0), 10); |
| 644 | if ((tz == LONG_MIN(-9223372036854775807L -1L) || tz == LONG_MAX9223372036854775807L) && errno(*__error()) == ERANGE34) |
| 645 | goto bad; |
| 646 | v->s = xstrdup(show_date(timestamp, tz, &date_mode)); |
| 647 | v->ul = timestamp; |
| 648 | return; |
| 649 | bad: |
| 650 | v->s = ""; |
| 651 | v->ul = 0; |
| 652 | } |
| 653 | |
| 654 | /* See grab_values */ |
| 655 | static void grab_person(const char *who, struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz) |
| 656 | { |
| 657 | int i; |
| 658 | int wholen = strlen(who); |
| 659 | const char *wholine = NULL((void*)0); |
| 660 | |
| 661 | for (i = 0; i < used_atom_cnt; i++) { |
| 662 | const char *name = used_atom[i].name; |
| 663 | struct atom_value *v = &val[i]; |
| 664 | if (!!deref != (*name == '*')) |
| 665 | continue; |
| 666 | if (deref) |
| 667 | name++; |
| 668 | if (strncmp(who, name, wholen)) |
| 669 | continue; |
| 670 | if (name[wholen] != 0 && |
| 671 | strcmp(name + wholen, "name") && |
| 672 | strcmp(name + wholen, "email") && |
| 673 | !starts_with(name + wholen, "date")) |
| 674 | continue; |
| 675 | if (!wholine) |
| 676 | wholine = find_wholine(who, wholen, buf, sz); |
| 677 | if (!wholine) |
| 678 | return; /* no point looking for it */ |
| 679 | if (name[wholen] == 0) |
| 680 | v->s = copy_line(wholine); |
| 681 | else if (!strcmp(name + wholen, "name")) |
| 682 | v->s = copy_name(wholine); |
| 683 | else if (!strcmp(name + wholen, "email")) |
| 684 | v->s = copy_email(wholine); |
| 685 | else if (starts_with(name + wholen, "date")) |
| 686 | grab_date(wholine, v, name); |
| 687 | } |
| 688 | |
| 689 | /* |
| 690 | * For a tag or a commit object, if "creator" or "creatordate" is |
| 691 | * requested, do something special. |
| 692 | */ |
| 693 | if (strcmp(who, "tagger") && strcmp(who, "committer")) |
| 694 | return; /* "author" for commit object is not wanted */ |
| 695 | if (!wholine) |
| 696 | wholine = find_wholine(who, wholen, buf, sz); |
| 697 | if (!wholine) |
| 698 | return; |
| 699 | for (i = 0; i < used_atom_cnt; i++) { |
| 700 | const char *name = used_atom[i].name; |
| 701 | struct atom_value *v = &val[i]; |
| 702 | if (!!deref != (*name == '*')) |
| 703 | continue; |
| 704 | if (deref) |
| 705 | name++; |
| 706 | |
| 707 | if (starts_with(name, "creatordate")) |
| 708 | grab_date(wholine, v, name); |
| 709 | else if (!strcmp(name, "creator")) |
| 710 | v->s = copy_line(wholine); |
| 711 | } |
| 712 | } |
| 713 | |
| 714 | static void find_subpos(const char *buf, unsigned long sz, |
| 715 | const char **sub, unsigned long *sublen, |
| 716 | const char **body, unsigned long *bodylen, |
| 717 | unsigned long *nonsiglen, |
| 718 | const char **sig, unsigned long *siglen) |
| 719 | { |
| 720 | const char *eol; |
| 721 | /* skip past header until we hit empty line */ |
| 722 | while (*buf && *buf != '\n') { |
| 723 | eol = strchrnulgitstrchrnul(buf, '\n'); |
| 724 | if (*eol) |
| 725 | eol++; |
| 726 | buf = eol; |
| 727 | } |
| 728 | /* skip any empty lines */ |
| 729 | while (*buf == '\n') |
| 730 | buf++; |
| 731 | |
| 732 | /* parse signature first; we might not even have a subject line */ |
| 733 | *sig = buf + parse_signature(buf, strlen(buf)); |
| 734 | *siglen = strlen(*sig); |
| 735 | |
| 736 | /* subject is first non-empty line */ |
| 737 | *sub = buf; |
| 738 | /* subject goes to first empty line */ |
| 739 | while (buf < *sig && *buf && *buf != '\n') { |
| 740 | eol = strchrnulgitstrchrnul(buf, '\n'); |
| 741 | if (*eol) |
| 742 | eol++; |
| 743 | buf = eol; |
| 744 | } |
| 745 | *sublen = buf - *sub; |
| 746 | /* drop trailing newline, if present */ |
| 747 | if (*sublen && (*sub)[*sublen - 1] == '\n') |
| 748 | *sublen -= 1; |
| 749 | |
| 750 | /* skip any empty lines */ |
| 751 | while (*buf == '\n') |
| 752 | buf++; |
| 753 | *body = buf; |
| 754 | *bodylen = strlen(buf); |
| 755 | *nonsiglen = *sig - buf; |
| 756 | } |
| 757 | |
| 758 | /* |
| 759 | * If 'lines' is greater than 0, append that many lines from the given |
| 760 | * 'buf' of length 'size' to the given strbuf. |
| 761 | */ |
| 762 | static void append_lines(struct strbuf *out, const char *buf, unsigned long size, int lines) |
| 763 | { |
| 764 | int i; |
| 765 | const char *sp, *eol; |
| 766 | size_t len; |
| 767 | |
| 768 | sp = buf; |
| 769 | |
| 770 | for (i = 0; i < lines && sp < buf + size; i++) { |
| 771 | if (i) |
| 772 | strbuf_addstr(out, "\n "); |
| 773 | eol = memchr(sp, '\n', size - (sp - buf)); |
| 774 | len = eol ? eol - sp : size - (sp - buf); |
| 775 | strbuf_add(out, sp, len); |
| 776 | if (!eol) |
| 777 | break; |
| 778 | sp = eol + 1; |
| 779 | } |
| 780 | } |
| 781 | |
| 782 | /* See grab_values */ |
| 783 | static void grab_sub_body_contents(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz) |
| 784 | { |
| 785 | int i; |
| 786 | const char *subpos = NULL((void*)0), *bodypos = NULL((void*)0), *sigpos = NULL((void*)0); |
| 787 | unsigned long sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0; |
| 788 | |
| 789 | for (i = 0; i < used_atom_cnt; i++) { |
| 790 | struct used_atom *atom = &used_atom[i]; |
| 791 | const char *name = atom->name; |
| 792 | struct atom_value *v = &val[i]; |
| 793 | if (!!deref != (*name == '*')) |
| 794 | continue; |
| 795 | if (deref) |
| 796 | name++; |
| 797 | if (strcmp(name, "subject") && |
| 798 | strcmp(name, "body") && |
| 799 | strcmp(name, "trailers") && |
| 800 | !starts_with(name, "contents")) |
| 801 | continue; |
| 802 | if (!subpos) |
| 803 | find_subpos(buf, sz, |
| 804 | &subpos, &sublen, |
| 805 | &bodypos, &bodylen, &nonsiglen, |
| 806 | &sigpos, &siglen); |
| 807 | |
| 808 | if (atom->u.contents.option == C_SUB) |
| 809 | v->s = copy_subject(subpos, sublen); |
| 810 | else if (atom->u.contents.option == C_BODY_DEP) |
| 811 | v->s = xmemdupz(bodypos, bodylen); |
| 812 | else if (atom->u.contents.option == C_BODY) |
| 813 | v->s = xmemdupz(bodypos, nonsiglen); |
| 814 | else if (atom->u.contents.option == C_SIG) |
| 815 | v->s = xmemdupz(sigpos, siglen); |
| 816 | else if (atom->u.contents.option == C_LINES) { |
| 817 | struct strbuf s = STRBUF_INIT{ 0, 0, strbuf_slopbuf }; |
| 818 | const char *contents_end = bodylen + bodypos - siglen; |
| 819 | |
| 820 | /* Size is the length of the message after removing the signature */ |
| 821 | append_lines(&s, subpos, contents_end - subpos, atom->u.contents.nlines); |
| 822 | v->s = strbuf_detach(&s, NULL((void*)0)); |
| 823 | } else if (atom->u.contents.option == C_TRAILERS) { |
| 824 | struct trailer_info info; |
| 825 | |
| 826 | /* Search for trailer info */ |
| 827 | trailer_info_get(&info, subpos); |
| 828 | v->s = xmemdupz(info.trailer_start, |
| 829 | info.trailer_end - info.trailer_start); |
| 830 | trailer_info_release(&info); |
| 831 | } else if (atom->u.contents.option == C_BARE) |
| 832 | v->s = xstrdup(subpos); |
| 833 | } |
| 834 | } |
| 835 | |
| 836 | /* |
| 837 | * We want to have empty print-string for field requests |
| 838 | * that do not apply (e.g. "authordate" for a tag object) |
| 839 | */ |
| 840 | static void fill_missing_values(struct atom_value *val) |
| 841 | { |
| 842 | int i; |
| 843 | for (i = 0; i < used_atom_cnt; i++) { |
| 844 | struct atom_value *v = &val[i]; |
| 845 | if (v->s == NULL((void*)0)) |
| 846 | v->s = ""; |
| 847 | } |
| 848 | } |
| 849 | |
| 850 | /* |
| 851 | * val is a list of atom_value to hold returned values. Extract |
| 852 | * the values for atoms in used_atom array out of (obj, buf, sz). |
| 853 | * when deref is false, (obj, buf, sz) is the object that is |
| 854 | * pointed at by the ref itself; otherwise it is the object the |
| 855 | * ref (which is a tag) refers to. |
| 856 | */ |
| 857 | static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz) |
| 858 | { |
| 859 | grab_common_values(val, deref, obj, buf, sz); |
| 860 | switch (obj->type) { |
| 861 | case OBJ_TAG: |
| 862 | grab_tag_values(val, deref, obj, buf, sz); |
| 863 | grab_sub_body_contents(val, deref, obj, buf, sz); |
| 864 | grab_person("tagger", val, deref, obj, buf, sz); |
| 865 | break; |
| 866 | case OBJ_COMMIT: |
| 867 | grab_commit_values(val, deref, obj, buf, sz); |
| 868 | grab_sub_body_contents(val, deref, obj, buf, sz); |
| 869 | grab_person("author", val, deref, obj, buf, sz); |
| 870 | grab_person("committer", val, deref, obj, buf, sz); |
| 871 | break; |
| 872 | case OBJ_TREE: |
| 873 | /* grab_tree_values(val, deref, obj, buf, sz); */ |
| 874 | break; |
| 875 | case OBJ_BLOB: |
| 876 | /* grab_blob_values(val, deref, obj, buf, sz); */ |
| 877 | break; |
| 878 | default: |
| 879 | die("Eh? Object of type %d?", obj->type); |
| 880 | } |
| 881 | } |
| 882 | |
| 883 | static inline char *copy_advance(char *dst, const char *src) |
| 884 | { |
| 885 | while (*src) |
| 886 | *dst++ = *src++; |
| 887 | return dst; |
| 888 | } |
| 889 | |
| 890 | static const char *strip_ref_components(const char *refname, const char *nr_arg) |
| 891 | { |
| 892 | char *end; |
| 893 | long nr = strtol(nr_arg, &end, 10); |
| 894 | long remaining = nr; |
| 895 | const char *start = refname; |
| 896 | |
| 897 | if (nr < 1 || *end != '\0') |
| 898 | die(_(":strip= requires a positive integer argument")); |
| 899 | |
| 900 | while (remaining) { |
| 901 | switch (*start++) { |
| 902 | case '\0': |
| 903 | die(_("ref '%s' does not have %ld components to :strip"), |
| 904 | refname, nr); |
| 905 | case '/': |
| 906 | remaining--; |
| 907 | break; |
| 908 | } |
| 909 | } |
| 910 | return start; |
| 911 | } |
| 912 | |
| 913 | static void fill_remote_ref_details(struct used_atom *atom, const char *refname, |
| 914 | struct branch *branch, const char **s) |
| 915 | { |
| 916 | int num_ours, num_theirs; |
| 917 | if (atom->u.remote_ref == RR_SHORTEN) |
| 918 | *s = shorten_unambiguous_ref(refname, warn_ambiguous_refs); |
| 919 | else if (atom->u.remote_ref == RR_TRACK) { |
| 920 | if (stat_tracking_info(branch, &num_ours, |
| 921 | &num_theirs, NULL((void*)0))) |
| 922 | return; |
| 923 | |
| 924 | if (!num_ours && !num_theirs) |
| 925 | *s = ""; |
| 926 | else if (!num_ours) |
| 927 | *s = xstrfmt("[behind %d]", num_theirs); |
| 928 | else if (!num_theirs) |
| 929 | *s = xstrfmt("[ahead %d]", num_ours); |
| 930 | else |
| 931 | *s = xstrfmt("[ahead %d, behind %d]", |
| 932 | num_ours, num_theirs); |
| 933 | } else if (atom->u.remote_ref == RR_TRACKSHORT) { |
| 934 | if (stat_tracking_info(branch, &num_ours, |
| 935 | &num_theirs, NULL((void*)0))) |
| 936 | return; |
| 937 | |
| 938 | if (!num_ours && !num_theirs) |
| 939 | *s = "="; |
| 940 | else if (!num_ours) |
| 941 | *s = "<"; |
| 942 | else if (!num_theirs) |
| 943 | *s = ">"; |
| 944 | else |
| 945 | *s = "<>"; |
| 946 | } else /* RR_NORMAL */ |
| 947 | *s = refname; |
| 948 | } |
| 949 | |
| 950 | /* |
| 951 | * Parse the object referred by ref, and grab needed value. |
| 952 | */ |
| 953 | static void populate_value(struct ref_array_item *ref) |
| 954 | { |
| 955 | void *buf; |
| 956 | struct object *obj; |
| 957 | int eaten, i; |
| 958 | unsigned long size; |
| 959 | const unsigned char *tagged; |
| 960 | |
| 961 | ref->value = xcalloc(used_atom_cnt, sizeof(struct atom_value)); |
| 962 | |
| 963 | if (need_symref && (ref->flag & REF_ISSYMREF0x01) && !ref->symref) { |
| 964 | unsigned char unused1[20]; |
| 965 | ref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING0x01, |
| 966 | unused1, NULL((void*)0)); |
| 967 | if (!ref->symref) |
| 968 | ref->symref = ""; |
| 969 | } |
| 970 | |
| 971 | /* Fill in specials first */ |
| 972 | for (i = 0; i < used_atom_cnt; i++) { |
| 973 | struct used_atom *atom = &used_atom[i]; |
| 974 | const char *name = used_atom[i].name; |
| 975 | struct atom_value *v = &ref->value[i]; |
| 976 | int deref = 0; |
| 977 | const char *refname; |
| 978 | const char *formatp; |
| 979 | struct branch *branch = NULL((void*)0); |
| 980 | |
| 981 | v->handler = append_atom; |
| 982 | |
| 983 | if (*name == '*') { |
| 984 | deref = 1; |
| 985 | name++; |
| 986 | } |
| 987 | |
| 988 | if (starts_with(name, "refname")) |
| 989 | refname = ref->refname; |
| 990 | else if (starts_with(name, "symref")) |
| 991 | refname = ref->symref ? ref->symref : ""; |
| 992 | else if (starts_with(name, "upstream")) { |
| 993 | const char *branch_name; |
| 994 | /* only local branches may have an upstream */ |
| 995 | if (!skip_prefix(ref->refname, "refs/heads/", |
| 996 | &branch_name)) |
| 997 | continue; |
| 998 | branch = branch_get(branch_name); |
| 999 | |
| 1000 | refname = branch_get_upstream(branch, NULL((void*)0)); |
| 1001 | if (refname) |
| 1002 | fill_remote_ref_details(atom, refname, branch, &v->s); |
| 1003 | continue; |
| 1004 | } else if (starts_with(name, "push")) { |
| 1005 | const char *branch_name; |
| 1006 | if (!skip_prefix(ref->refname, "refs/heads/", |
| 1007 | &branch_name)) |
| 1008 | continue; |
| 1009 | branch = branch_get(branch_name); |
| 1010 | |
| 1011 | refname = branch_get_push(branch, NULL((void*)0)); |
| 1012 | if (!refname) |
| 1013 | continue; |
| 1014 | fill_remote_ref_details(atom, refname, branch, &v->s); |
| 1015 | continue; |
| 1016 | } else if (starts_with(name, "color:")) { |
| 1017 | v->s = atom->u.color; |
| 1018 | continue; |
| 1019 | } else if (!strcmp(name, "flag")) { |
| 1020 | char buf[256], *cp = buf; |
| 1021 | if (ref->flag & REF_ISSYMREF0x01) |
| 1022 | cp = copy_advance(cp, ",symref"); |
| 1023 | if (ref->flag & REF_ISPACKED0x02) |
| 1024 | cp = copy_advance(cp, ",packed"); |
| 1025 | if (cp == buf) |
| 1026 | v->s = ""; |
| 1027 | else { |
| 1028 | *cp = '\0'; |
| 1029 | v->s = xstrdup(buf + 1); |
| 1030 | } |
| 1031 | continue; |
| 1032 | } else if (!deref && grab_objectname(name, ref->objectname, v, atom)) { |
| 1033 | continue; |
| 1034 | } else if (!strcmp(name, "HEAD")) { |
| 1035 | const char *head; |
| 1036 | unsigned char sha1[20]; |
| 1037 | |
| 1038 | head = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING0x01, |
| 1039 | sha1, NULL((void*)0)); |
| 1040 | if (head && !strcmp(ref->refname, head)) |
| 1041 | v->s = "*"; |
| 1042 | else |
| 1043 | v->s = " "; |
| 1044 | continue; |
| 1045 | } else if (starts_with(name, "align")) { |
| 1046 | v->u.align = atom->u.align; |
| 1047 | v->handler = align_atom_handler; |
| 1048 | continue; |
| 1049 | } else if (!strcmp(name, "end")) { |
| 1050 | v->handler = end_atom_handler; |
| 1051 | continue; |
| 1052 | } else |
| 1053 | continue; |
| 1054 | |
| 1055 | formatp = strchr(name, ':'); |
| 1056 | if (formatp) { |
| 1057 | const char *arg; |
| 1058 | |
| 1059 | formatp++; |
| 1060 | if (!strcmp(formatp, "short")) |
| 1061 | refname = shorten_unambiguous_ref(refname, |
| 1062 | warn_ambiguous_refs); |
| 1063 | else if (skip_prefix(formatp, "strip=", &arg)) |
| 1064 | refname = strip_ref_components(refname, arg); |
| 1065 | else |
| 1066 | die(_("unknown %.*s format %s"), |
| 1067 | (int)(formatp - name), name, formatp); |
| 1068 | } |
| 1069 | |
| 1070 | if (!deref) |
| 1071 | v->s = refname; |
| 1072 | else |
| 1073 | v->s = xstrfmt("%s^{}", refname); |
| 1074 | } |
| 1075 | |
| 1076 | for (i = 0; i < used_atom_cnt; i++) { |
| 1077 | struct atom_value *v = &ref->value[i]; |
| 1078 | if (v->s == NULL((void*)0)) |
| 1079 | goto need_obj; |
| 1080 | } |
| 1081 | return; |
| 1082 | |
| 1083 | need_obj: |
| 1084 | buf = get_obj(ref->objectname, &obj, &size, &eaten); |
| 1085 | if (!buf) |
| 1086 | die(_("missing object %s for %s"), |
| 1087 | sha1_to_hex(ref->objectname), ref->refname); |
| 1088 | if (!obj) |
| 1089 | die(_("parse_object_buffer failed on %s for %s"), |
| 1090 | sha1_to_hex(ref->objectname), ref->refname); |
| 1091 | |
| 1092 | grab_values(ref->value, 0, obj, buf, size); |
| 1093 | if (!eaten) |
| 1094 | free(buf); |
| 1095 | |
| 1096 | /* |
| 1097 | * If there is no atom that wants to know about tagged |
| 1098 | * object, we are done. |
| 1099 | */ |
| 1100 | if (!need_tagged || (obj->type != OBJ_TAG)) |
| 1101 | return; |
| 1102 | |
| 1103 | /* |
| 1104 | * If it is a tag object, see if we use a value that derefs |
| 1105 | * the object, and if we do grab the object it refers to. |
| 1106 | */ |
| 1107 | tagged = ((struct tag *)obj)->tagged->oid.hash; |
| 1108 | |
| 1109 | /* |
| 1110 | * NEEDSWORK: This derefs tag only once, which |
| 1111 | * is good to deal with chains of trust, but |
| 1112 | * is not consistent with what deref_tag() does |
| 1113 | * which peels the onion to the core. |
| 1114 | */ |
| 1115 | buf = get_obj(tagged, &obj, &size, &eaten); |
| 1116 | if (!buf) |
| 1117 | die(_("missing object %s for %s"), |
| 1118 | sha1_to_hex(tagged), ref->refname); |
| 1119 | if (!obj) |
| 1120 | die(_("parse_object_buffer failed on %s for %s"), |
| 1121 | sha1_to_hex(tagged), ref->refname); |
| 1122 | grab_values(ref->value, 1, obj, buf, size); |
| 1123 | if (!eaten) |
| 1124 | free(buf); |
| 1125 | } |
| 1126 | |
| 1127 | /* |
| 1128 | * Given a ref, return the value for the atom. This lazily gets value |
| 1129 | * out of the object by calling populate value. |
| 1130 | */ |
| 1131 | static void get_ref_atom_value(struct ref_array_item *ref, int atom, struct atom_value **v) |
| 1132 | { |
| 1133 | if (!ref->value) { |
| 1134 | populate_value(ref); |
| 1135 | fill_missing_values(ref->value); |
| 1136 | } |
| 1137 | *v = &ref->value[atom]; |
| 1138 | } |
| 1139 | |
| 1140 | enum contains_result { |
| 1141 | CONTAINS_UNKNOWN = -1, |
| 1142 | CONTAINS_NO = 0, |
| 1143 | CONTAINS_YES = 1 |
| 1144 | }; |
| 1145 | |
| 1146 | /* |
| 1147 | * Mimicking the real stack, this stack lives on the heap, avoiding stack |
| 1148 | * overflows. |
| 1149 | * |
| 1150 | * At each recursion step, the stack items points to the commits whose |
| 1151 | * ancestors are to be inspected. |
| 1152 | */ |
| 1153 | struct contains_stack { |
| 1154 | int nr, alloc; |
| 1155 | struct contains_stack_entry { |
| 1156 | struct commit *commit; |
| 1157 | struct commit_list *parents; |
| 1158 | } *contains_stack; |
| 1159 | }; |
| 1160 | |
| 1161 | static int in_commit_list(const struct commit_list *want, struct commit *c) |
| 1162 | { |
| 1163 | for (; want; want = want->next) |
| 1164 | if (!oidcmp(&want->item->object.oid, &c->object.oid)) |
| 1165 | return 1; |
| 1166 | return 0; |
| 1167 | } |
| 1168 | |
| 1169 | /* |
| 1170 | * Test whether the candidate or one of its parents is contained in the list. |
| 1171 | * Do not recurse to find out, though, but return -1 if inconclusive. |
| 1172 | */ |
| 1173 | static enum contains_result contains_test(struct commit *candidate, |
| 1174 | const struct commit_list *want) |
| 1175 | { |
| 1176 | /* was it previously marked as containing a want commit? */ |
| 1177 | if (candidate->object.flags & TMP_MARK(1u<<4)) |
| 1178 | return 1; |
| 1179 | /* or marked as not possibly containing a want commit? */ |
| 1180 | if (candidate->object.flags & UNINTERESTING(1u<<1)) |
| 1181 | return 0; |
| 1182 | /* or are we it? */ |
| 1183 | if (in_commit_list(want, candidate)) { |
| 1184 | candidate->object.flags |= TMP_MARK(1u<<4); |
| 1185 | return 1; |
| 1186 | } |
| 1187 | |
| 1188 | if (parse_commit(candidate) < 0) |
| 1189 | return 0; |
| 1190 | |
| 1191 | return -1; |
| 1192 | } |
| 1193 | |
| 1194 | static void push_to_contains_stack(struct commit *candidate, struct contains_stack *contains_stack) |
| 1195 | { |
| 1196 | ALLOC_GROW(contains_stack->contains_stack, contains_stack->nr + 1, contains_stack->alloc)do { if ((contains_stack->nr + 1) > contains_stack-> alloc) { if ((((contains_stack->alloc)+16)*3/2) < (contains_stack ->nr + 1)) contains_stack->alloc = (contains_stack-> nr + 1); else contains_stack->alloc = (((contains_stack-> alloc)+16)*3/2); (contains_stack->contains_stack) = xrealloc ((contains_stack->contains_stack), st_mult(sizeof(*(contains_stack ->contains_stack)), (contains_stack->alloc))); } } while (0); |
| 1197 | contains_stack->contains_stack[contains_stack->nr].commit = candidate; |
| 1198 | contains_stack->contains_stack[contains_stack->nr++].parents = candidate->parents; |
| 1199 | } |
| 1200 | |
| 1201 | static enum contains_result contains_tag_algo(struct commit *candidate, |
| 1202 | const struct commit_list *want) |
| 1203 | { |
| 1204 | struct contains_stack contains_stack = { 0, 0, NULL((void*)0) }; |
| 1205 | int result = contains_test(candidate, want); |
| 1206 | |
| 1207 | if (result != CONTAINS_UNKNOWN) |
| 1208 | return result; |
| 1209 | |
| 1210 | push_to_contains_stack(candidate, &contains_stack); |
| 1211 | while (contains_stack.nr) { |
| 1212 | struct contains_stack_entry *entry = &contains_stack.contains_stack[contains_stack.nr - 1]; |
| 1213 | struct commit *commit = entry->commit; |
| 1214 | struct commit_list *parents = entry->parents; |
| 1215 | |
| 1216 | if (!parents) { |
| 1217 | commit->object.flags |= UNINTERESTING(1u<<1); |
| 1218 | contains_stack.nr--; |
| 1219 | } |
| 1220 | /* |
| 1221 | * If we just popped the stack, parents->item has been marked, |
| 1222 | * therefore contains_test will return a meaningful 0 or 1. |
| 1223 | */ |
| 1224 | else switch (contains_test(parents->item, want)) { |
| 1225 | case CONTAINS_YES: |
| 1226 | commit->object.flags |= TMP_MARK(1u<<4); |
| 1227 | contains_stack.nr--; |
| 1228 | break; |
| 1229 | case CONTAINS_NO: |
| 1230 | entry->parents = parents->next; |
| 1231 | break; |
| 1232 | case CONTAINS_UNKNOWN: |
| 1233 | push_to_contains_stack(parents->item, &contains_stack); |
| 1234 | break; |
| 1235 | } |
| 1236 | } |
| 1237 | free(contains_stack.contains_stack); |
| 1238 | return contains_test(candidate, want); |
| 1239 | } |
| 1240 | |
| 1241 | static int commit_contains(struct ref_filter *filter, struct commit *commit) |
| 1242 | { |
| 1243 | if (filter->with_commit_tag_algo) |
| 1244 | return contains_tag_algo(commit, filter->with_commit); |
| 1245 | return is_descendant_of(commit, filter->with_commit); |
| 1246 | } |
| 1247 | |
| 1248 | /* |
| 1249 | * Return 1 if the refname matches one of the patterns, otherwise 0. |
| 1250 | * A pattern can be a literal prefix (e.g. a refname "refs/heads/master" |
| 1251 | * matches a pattern "refs/heads/mas") or a wildcard (e.g. the same ref |
| 1252 | * matches "refs/heads/mas*", too). |
| 1253 | */ |
| 1254 | static int match_pattern(const struct ref_filter *filter, const char *refname) |
| 1255 | { |
| 1256 | const char **patterns = filter->name_patterns; |
| 1257 | unsigned flags = 0; |
| 1258 | |
| 1259 | if (filter->ignore_case) |
| 1260 | flags |= WM_CASEFOLD1; |
| 1261 | |
| 1262 | /* |
| 1263 | * When no '--format' option is given we need to skip the prefix |
| 1264 | * for matching refs of tags and branches. |
| 1265 | */ |
| 1266 | (void)(skip_prefix(refname, "refs/tags/", &refname) || |
| 1267 | skip_prefix(refname, "refs/heads/", &refname) || |
| 1268 | skip_prefix(refname, "refs/remotes/", &refname) || |
| 1269 | skip_prefix(refname, "refs/", &refname)); |
| 1270 | |
| 1271 | for (; *patterns; patterns++) { |
| 1272 | if (!wildmatch(*patterns, refname, flags, NULL((void*)0))) |
| 1273 | return 1; |
| 1274 | } |
| 1275 | return 0; |
| 1276 | } |
| 1277 | |
| 1278 | /* |
| 1279 | * Return 1 if the refname matches one of the patterns, otherwise 0. |
| 1280 | * A pattern can be path prefix (e.g. a refname "refs/heads/master" |
| 1281 | * matches a pattern "refs/heads/" but not "refs/heads/m") or a |
| 1282 | * wildcard (e.g. the same ref matches "refs/heads/m*", too). |
| 1283 | */ |
| 1284 | static int match_name_as_path(const struct ref_filter *filter, const char *refname) |
| 1285 | { |
| 1286 | const char **pattern = filter->name_patterns; |
| 1287 | int namelen = strlen(refname); |
| 1288 | unsigned flags = WM_PATHNAME2; |
| 1289 | |
| 1290 | if (filter->ignore_case) |
| 1291 | flags |= WM_CASEFOLD1; |
Value stored to 'flags' is never read | |
| 1292 | |
| 1293 | for (; *pattern; pattern++) { |
| 1294 | const char *p = *pattern; |
| 1295 | int plen = strlen(p); |
| 1296 | |
| 1297 | if ((plen <= namelen) && |
| 1298 | !strncmp(refname, p, plen) && |
| 1299 | (refname[plen] == '\0' || |
| 1300 | refname[plen] == '/' || |
| 1301 | p[plen-1] == '/')) |
| 1302 | return 1; |
| 1303 | if (!wildmatch(p, refname, WM_PATHNAME2, NULL((void*)0))) |
| 1304 | return 1; |
| 1305 | } |
| 1306 | return 0; |
| 1307 | } |
| 1308 | |
| 1309 | /* Return 1 if the refname matches one of the patterns, otherwise 0. */ |
| 1310 | static int filter_pattern_match(struct ref_filter *filter, const char *refname) |
| 1311 | { |
| 1312 | if (!*filter->name_patterns) |
| 1313 | return 1; /* No pattern always matches */ |
| 1314 | if (filter->match_as_path) |
| 1315 | return match_name_as_path(filter, refname); |
| 1316 | return match_pattern(filter, refname); |
| 1317 | } |
| 1318 | |
| 1319 | /* |
| 1320 | * Given a ref (sha1, refname), check if the ref belongs to the array |
| 1321 | * of sha1s. If the given ref is a tag, check if the given tag points |
| 1322 | * at one of the sha1s in the given sha1 array. |
| 1323 | * the given sha1_array. |
| 1324 | * NEEDSWORK: |
| 1325 | * 1. Only a single level of inderection is obtained, we might want to |
| 1326 | * change this to account for multiple levels (e.g. annotated tags |
| 1327 | * pointing to annotated tags pointing to a commit.) |
| 1328 | * 2. As the refs are cached we might know what refname peels to without |
| 1329 | * the need to parse the object via parse_object(). peel_ref() might be a |
| 1330 | * more efficient alternative to obtain the pointee. |
| 1331 | */ |
| 1332 | static const unsigned char *match_points_at(struct sha1_array *points_at, |
| 1333 | const unsigned char *sha1, |
| 1334 | const char *refname) |
| 1335 | { |
| 1336 | const unsigned char *tagged_sha1 = NULL((void*)0); |
| 1337 | struct object *obj; |
| 1338 | |
| 1339 | if (sha1_array_lookup(points_at, sha1) >= 0) |
| 1340 | return sha1; |
| 1341 | obj = parse_object(sha1); |
| 1342 | if (!obj) |
| 1343 | die(_("malformed object at '%s'"), refname); |
| 1344 | if (obj->type == OBJ_TAG) |
| 1345 | tagged_sha1 = ((struct tag *)obj)->tagged->oid.hash; |
| 1346 | if (tagged_sha1 && sha1_array_lookup(points_at, tagged_sha1) >= 0) |
| 1347 | return tagged_sha1; |
| 1348 | return NULL((void*)0); |
| 1349 | } |
| 1350 | |
| 1351 | /* Allocate space for a new ref_array_item and copy the objectname and flag to it */ |
| 1352 | static struct ref_array_item *new_ref_array_item(const char *refname, |
| 1353 | const unsigned char *objectname, |
| 1354 | int flag) |
| 1355 | { |
| 1356 | struct ref_array_item *ref; |
| 1357 | FLEX_ALLOC_STR(ref, refname, refname)do { size_t flex_array_len_ = (strlen(refname)); ((ref)) = xcalloc (1, st_add(st_add((sizeof(*((ref)))),(flex_array_len_)),(1))) ; __builtin___memcpy_chk ((void *)((ref))->refname, ((refname )), flex_array_len_, __builtin_object_size ((void *)((ref))-> refname, 0)); } while (0); |
| 1358 | hashcpy(ref->objectname, objectname); |
| 1359 | ref->flag = flag; |
| 1360 | |
| 1361 | return ref; |
| 1362 | } |
| 1363 | |
| 1364 | static int ref_kind_from_refname(const char *refname) |
| 1365 | { |
| 1366 | unsigned int i; |
| 1367 | |
| 1368 | static struct { |
| 1369 | const char *prefix; |
| 1370 | unsigned int kind; |
| 1371 | } ref_kind[] = { |
| 1372 | { "refs/heads/" , FILTER_REFS_BRANCHES0x0004 }, |
| 1373 | { "refs/remotes/" , FILTER_REFS_REMOTES0x0008 }, |
| 1374 | { "refs/tags/", FILTER_REFS_TAGS0x0002} |
| 1375 | }; |
| 1376 | |
| 1377 | if (!strcmp(refname, "HEAD")) |
| 1378 | return FILTER_REFS_DETACHED_HEAD0x0020; |
| 1379 | |
| 1380 | for (i = 0; i < ARRAY_SIZE(ref_kind)(sizeof(ref_kind) / sizeof((ref_kind)[0]) + (sizeof(char [1 - 2*!(!__builtin_types_compatible_p(__typeof__(ref_kind), __typeof__ (&(ref_kind)[0])))]) - 1)); i++) { |
| 1381 | if (starts_with(refname, ref_kind[i].prefix)) |
| 1382 | return ref_kind[i].kind; |
| 1383 | } |
| 1384 | |
| 1385 | return FILTER_REFS_OTHERS0x0010; |
| 1386 | } |
| 1387 | |
| 1388 | static int filter_ref_kind(struct ref_filter *filter, const char *refname) |
| 1389 | { |
| 1390 | if (filter->kind == FILTER_REFS_BRANCHES0x0004 || |
| 1391 | filter->kind == FILTER_REFS_REMOTES0x0008 || |
| 1392 | filter->kind == FILTER_REFS_TAGS0x0002) |
| 1393 | return filter->kind; |
| 1394 | return ref_kind_from_refname(refname); |
| 1395 | } |
| 1396 | |
| 1397 | /* |
| 1398 | * A call-back given to for_each_ref(). Filter refs and keep them for |
| 1399 | * later object processing. |
| 1400 | */ |
| 1401 | static int ref_filter_handler(const char *refname, const struct object_id *oid, int flag, void *cb_data) |
| 1402 | { |
| 1403 | struct ref_filter_cbdata *ref_cbdata = cb_data; |
| 1404 | struct ref_filter *filter = ref_cbdata->filter; |
| 1405 | struct ref_array_item *ref; |
| 1406 | struct commit *commit = NULL((void*)0); |
| 1407 | unsigned int kind; |
| 1408 | |
| 1409 | if (flag & REF_BAD_NAME0x08) { |
| 1410 | warning(_("ignoring ref with broken name %s"), refname); |
| 1411 | return 0; |
| 1412 | } |
| 1413 | |
| 1414 | if (flag & REF_ISBROKEN0x04) { |
| 1415 | warning(_("ignoring broken ref %s"), refname); |
| 1416 | return 0; |
| 1417 | } |
| 1418 | |
| 1419 | /* Obtain the current ref kind from filter_ref_kind() and ignore unwanted refs. */ |
| 1420 | kind = filter_ref_kind(filter, refname); |
| 1421 | if (!(kind & filter->kind)) |
| 1422 | return 0; |
| 1423 | |
| 1424 | if (!filter_pattern_match(filter, refname)) |
| 1425 | return 0; |
| 1426 | |
| 1427 | if (filter->points_at.nr && !match_points_at(&filter->points_at, oid->hash, refname)) |
| 1428 | return 0; |
| 1429 | |
| 1430 | /* |
| 1431 | * A merge filter is applied on refs pointing to commits. Hence |
| 1432 | * obtain the commit using the 'oid' available and discard all |
| 1433 | * non-commits early. The actual filtering is done later. |
| 1434 | */ |
| 1435 | if (filter->merge_commit || filter->with_commit || filter->verbose) { |
| 1436 | commit = lookup_commit_reference_gently(oid->hash, 1); |
| 1437 | if (!commit) |
| 1438 | return 0; |
| 1439 | /* We perform the filtering for the '--contains' option */ |
| 1440 | if (filter->with_commit && |
| 1441 | !commit_contains(filter, commit)) |
| 1442 | return 0; |
| 1443 | } |
| 1444 | |
| 1445 | /* |
| 1446 | * We do not open the object yet; sort may only need refname |
| 1447 | * to do its job and the resulting list may yet to be pruned |
| 1448 | * by maxcount logic. |
| 1449 | */ |
| 1450 | ref = new_ref_array_item(refname, oid->hash, flag); |
| 1451 | ref->commit = commit; |
| 1452 | |
| 1453 | REALLOC_ARRAY(ref_cbdata->array->items, ref_cbdata->array->nr + 1)(ref_cbdata->array->items) = xrealloc((ref_cbdata->array ->items), st_mult(sizeof(*(ref_cbdata->array->items) ), (ref_cbdata->array->nr + 1))); |
| 1454 | ref_cbdata->array->items[ref_cbdata->array->nr++] = ref; |
| 1455 | ref->kind = kind; |
| 1456 | return 0; |
| 1457 | } |
| 1458 | |
| 1459 | /* Free memory allocated for a ref_array_item */ |
| 1460 | static void free_array_item(struct ref_array_item *item) |
| 1461 | { |
| 1462 | free((char *)item->symref); |
| 1463 | free(item); |
| 1464 | } |
| 1465 | |
| 1466 | /* Free all memory allocated for ref_array */ |
| 1467 | void ref_array_clear(struct ref_array *array) |
| 1468 | { |
| 1469 | int i; |
| 1470 | |
| 1471 | for (i = 0; i < array->nr; i++) |
| 1472 | free_array_item(array->items[i]); |
| 1473 | free(array->items); |
| 1474 | array->items = NULL((void*)0); |
| 1475 | array->nr = array->alloc = 0; |
| 1476 | } |
| 1477 | |
| 1478 | static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata) |
| 1479 | { |
| 1480 | struct rev_info revs; |
| 1481 | int i, old_nr; |
| 1482 | struct ref_filter *filter = ref_cbdata->filter; |
| 1483 | struct ref_array *array = ref_cbdata->array; |
| 1484 | struct commit **to_clear = xcalloc(sizeof(struct commit *), array->nr); |
| 1485 | |
| 1486 | init_revisions(&revs, NULL((void*)0)); |
| 1487 | |
| 1488 | for (i = 0; i < array->nr; i++) { |
| 1489 | struct ref_array_item *item = array->items[i]; |
| 1490 | add_pending_object(&revs, &item->commit->object, item->refname); |
| 1491 | to_clear[i] = item->commit; |
| 1492 | } |
| 1493 | |
| 1494 | filter->merge_commit->object.flags |= UNINTERESTING(1u<<1); |
| 1495 | add_pending_object(&revs, &filter->merge_commit->object, ""); |
| 1496 | |
| 1497 | revs.limited = 1; |
| 1498 | if (prepare_revision_walk(&revs)) |
| 1499 | die(_("revision walk setup failed")); |
| 1500 | |
| 1501 | old_nr = array->nr; |
| 1502 | array->nr = 0; |
| 1503 | |
| 1504 | for (i = 0; i < old_nr; i++) { |
| 1505 | struct ref_array_item *item = array->items[i]; |
| 1506 | struct commit *commit = item->commit; |
| 1507 | |
| 1508 | int is_merged = !!(commit->object.flags & UNINTERESTING(1u<<1)); |
| 1509 | |
| 1510 | if (is_merged == (filter->merge == REF_FILTER_MERGED_INCLUDE)) |
| 1511 | array->items[array->nr++] = array->items[i]; |
| 1512 | else |
| 1513 | free_array_item(item); |
| 1514 | } |
| 1515 | |
| 1516 | for (i = 0; i < old_nr; i++) |
| 1517 | clear_commit_marks(to_clear[i], ALL_REV_FLAGS(((1u<<11)-1) | (1u<<26))); |
| 1518 | clear_commit_marks(filter->merge_commit, ALL_REV_FLAGS(((1u<<11)-1) | (1u<<26))); |
| 1519 | free(to_clear); |
| 1520 | } |
| 1521 | |
| 1522 | /* |
| 1523 | * API for filtering a set of refs. Based on the type of refs the user |
| 1524 | * has requested, we iterate through those refs and apply filters |
| 1525 | * as per the given ref_filter structure and finally store the |
| 1526 | * filtered refs in the ref_array structure. |
| 1527 | */ |
| 1528 | int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type) |
| 1529 | { |
| 1530 | struct ref_filter_cbdata ref_cbdata; |
| 1531 | int ret = 0; |
| 1532 | unsigned int broken = 0; |
| 1533 | |
| 1534 | ref_cbdata.array = array; |
| 1535 | ref_cbdata.filter = filter; |
| 1536 | |
| 1537 | if (type & FILTER_REFS_INCLUDE_BROKEN0x0001) |
| 1538 | broken = 1; |
| 1539 | filter->kind = type & FILTER_REFS_KIND_MASK((0x0002 | 0x0004 | 0x0008 | 0x0010) | 0x0020); |
| 1540 | |
| 1541 | /* Simple per-ref filtering */ |
| 1542 | if (!filter->kind) |
| 1543 | die("filter_refs: invalid type"); |
| 1544 | else { |
| 1545 | /* |
| 1546 | * For common cases where we need only branches or remotes or tags, |
| 1547 | * we only iterate through those refs. If a mix of refs is needed, |
| 1548 | * we iterate over all refs and filter out required refs with the help |
| 1549 | * of filter_ref_kind(). |
| 1550 | */ |
| 1551 | if (filter->kind == FILTER_REFS_BRANCHES0x0004) |
| 1552 | ret = for_each_fullref_in("refs/heads/", ref_filter_handler, &ref_cbdata, broken); |
| 1553 | else if (filter->kind == FILTER_REFS_REMOTES0x0008) |
| 1554 | ret = for_each_fullref_in("refs/remotes/", ref_filter_handler, &ref_cbdata, broken); |
| 1555 | else if (filter->kind == FILTER_REFS_TAGS0x0002) |
| 1556 | ret = for_each_fullref_in("refs/tags/", ref_filter_handler, &ref_cbdata, broken); |
| 1557 | else if (filter->kind & FILTER_REFS_ALL(0x0002 | 0x0004 | 0x0008 | 0x0010)) |
| 1558 | ret = for_each_fullref_in("", ref_filter_handler, &ref_cbdata, broken); |
| 1559 | if (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD0x0020)) |
| 1560 | head_ref(ref_filter_handler, &ref_cbdata); |
| 1561 | } |
| 1562 | |
| 1563 | |
| 1564 | /* Filters that need revision walking */ |
| 1565 | if (filter->merge_commit) |
| 1566 | do_merge_filter(&ref_cbdata); |
| 1567 | |
| 1568 | return ret; |
| 1569 | } |
| 1570 | |
| 1571 | static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, struct ref_array_item *b) |
| 1572 | { |
| 1573 | struct atom_value *va, *vb; |
| 1574 | int cmp; |
| 1575 | cmp_type cmp_type = used_atom[s->atom].type; |
| 1576 | int (*cmp_fn)(const char *, const char *); |
| 1577 | |
| 1578 | get_ref_atom_value(a, s->atom, &va); |
| 1579 | get_ref_atom_value(b, s->atom, &vb); |
| 1580 | cmp_fn = s->ignore_case ? strcasecmp : strcmp; |
| 1581 | if (s->version) |
| 1582 | cmp = versioncmp(va->s, vb->s); |
| 1583 | else if (cmp_type == FIELD_STR) |
| 1584 | cmp = cmp_fn(va->s, vb->s); |
| 1585 | else { |
| 1586 | if (va->ul < vb->ul) |
| 1587 | cmp = -1; |
| 1588 | else if (va->ul == vb->ul) |
| 1589 | cmp = cmp_fn(a->refname, b->refname); |
| 1590 | else |
| 1591 | cmp = 1; |
| 1592 | } |
| 1593 | |
| 1594 | return (s->reverse) ? -cmp : cmp; |
| 1595 | } |
| 1596 | |
| 1597 | static int compare_refs(const void *a_, const void *b_, void *ref_sorting) |
| 1598 | { |
| 1599 | struct ref_array_item *a = *((struct ref_array_item **)a_); |
| 1600 | struct ref_array_item *b = *((struct ref_array_item **)b_); |
| 1601 | struct ref_sorting *s; |
| 1602 | |
| 1603 | for (s = ref_sorting; s; s = s->next) { |
| 1604 | int cmp = cmp_ref_sorting(s, a, b); |
| 1605 | if (cmp) |
| 1606 | return cmp; |
| 1607 | } |
| 1608 | return 0; |
| 1609 | } |
| 1610 | |
| 1611 | void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array) |
| 1612 | { |
| 1613 | QSORT_S(array->items, array->nr, compare_refs, sorting)do { if (git_qsort_s((array->items), (array->nr), sizeof (*(array->items)), compare_refs, sorting)) die("BUG: qsort_s() failed" ); } while (0); |
| 1614 | } |
| 1615 | |
| 1616 | static void append_literal(const char *cp, const char *ep, struct ref_formatting_state *state) |
| 1617 | { |
| 1618 | struct strbuf *s = &state->stack->output; |
| 1619 | |
| 1620 | while (*cp && (!ep || cp < ep)) { |
| 1621 | if (*cp == '%') { |
| 1622 | if (cp[1] == '%') |
| 1623 | cp++; |
| 1624 | else { |
| 1625 | int ch = hex2chr(cp + 1); |
| 1626 | if (0 <= ch) { |
| 1627 | strbuf_addch(s, ch); |
| 1628 | cp += 3; |
| 1629 | continue; |
| 1630 | } |
| 1631 | } |
| 1632 | } |
| 1633 | strbuf_addch(s, *cp); |
| 1634 | cp++; |
| 1635 | } |
| 1636 | } |
| 1637 | |
| 1638 | void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style) |
| 1639 | { |
| 1640 | const char *cp, *sp, *ep; |
| 1641 | struct strbuf *final_buf; |
| 1642 | struct ref_formatting_state state = REF_FORMATTING_STATE_INIT{ 0, ((void*)0) }; |
| 1643 | |
| 1644 | state.quote_style = quote_style; |
| 1645 | push_stack_element(&state.stack); |
| 1646 | |
| 1647 | for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) { |
| 1648 | struct atom_value *atomv; |
| 1649 | |
| 1650 | ep = strchr(sp, ')'); |
| 1651 | if (cp < sp) |
| 1652 | append_literal(cp, sp, &state); |
| 1653 | get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv); |
| 1654 | atomv->handler(atomv, &state); |
| 1655 | } |
| 1656 | if (*cp) { |
| 1657 | sp = cp + strlen(cp); |
| 1658 | append_literal(cp, sp, &state); |
| 1659 | } |
| 1660 | if (need_color_reset_at_eol) { |
| 1661 | struct atom_value resetv; |
| 1662 | char color[COLOR_MAXLEN75] = ""; |
| 1663 | |
| 1664 | if (color_parse("reset", color) < 0) |
| 1665 | die("BUG: couldn't parse 'reset' as a color"); |
| 1666 | resetv.s = color; |
| 1667 | append_atom(&resetv, &state); |
| 1668 | } |
| 1669 | if (state.stack->prev) |
| 1670 | die(_("format: %%(end) atom missing")); |
| 1671 | final_buf = &state.stack->output; |
| 1672 | fwrite(final_buf->buf, 1, final_buf->len, stdout__stdoutp); |
| 1673 | pop_stack_element(&state.stack); |
| 1674 | putchar('\n'); |
| 1675 | } |
| 1676 | |
| 1677 | void pretty_print_ref(const char *name, const unsigned char *sha1, |
| 1678 | const char *format) |
| 1679 | { |
| 1680 | struct ref_array_item *ref_item; |
| 1681 | ref_item = new_ref_array_item(name, sha1, 0); |
| 1682 | ref_item->kind = ref_kind_from_refname(name); |
| 1683 | show_ref_array_item(ref_item, format, 0); |
| 1684 | free_array_item(ref_item); |
| 1685 | } |
| 1686 | |
| 1687 | /* If no sorting option is given, use refname to sort as default */ |
| 1688 | struct ref_sorting *ref_default_sorting(void) |
| 1689 | { |
| 1690 | static const char cstr_name[] = "refname"; |
| 1691 | |
| 1692 | struct ref_sorting *sorting = xcalloc(1, sizeof(*sorting)); |
| 1693 | |
| 1694 | sorting->next = NULL((void*)0); |
| 1695 | sorting->atom = parse_ref_filter_atom(cstr_name, cstr_name + strlen(cstr_name)); |
| 1696 | return sorting; |
| 1697 | } |
| 1698 | |
| 1699 | int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset) |
| 1700 | { |
| 1701 | struct ref_sorting **sorting_tail = opt->value; |
| 1702 | struct ref_sorting *s; |
| 1703 | int len; |
| 1704 | |
| 1705 | if (!arg) /* should --no-sort void the list ? */ |
| 1706 | return -1; |
| 1707 | |
| 1708 | s = xcalloc(1, sizeof(*s)); |
| 1709 | s->next = *sorting_tail; |
| 1710 | *sorting_tail = s; |
| 1711 | |
| 1712 | if (*arg == '-') { |
| 1713 | s->reverse = 1; |
| 1714 | arg++; |
| 1715 | } |
| 1716 | if (skip_prefix(arg, "version:", &arg) || |
| 1717 | skip_prefix(arg, "v:", &arg)) |
| 1718 | s->version = 1; |
| 1719 | len = strlen(arg); |
| 1720 | s->atom = parse_ref_filter_atom(arg, arg+len); |
| 1721 | return 0; |
| 1722 | } |
| 1723 | |
| 1724 | int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset) |
| 1725 | { |
| 1726 | struct ref_filter *rf = opt->value; |
| 1727 | unsigned char sha1[20]; |
| 1728 | |
| 1729 | rf->merge = starts_with(opt->long_name, "no") |
| 1730 | ? REF_FILTER_MERGED_OMIT |
| 1731 | : REF_FILTER_MERGED_INCLUDE; |
| 1732 | |
| 1733 | if (get_sha1(arg, sha1)) |
| 1734 | die(_("malformed object name %s"), arg); |
| 1735 | |
| 1736 | rf->merge_commit = lookup_commit_reference_gently(sha1, 0); |
| 1737 | if (!rf->merge_commit) |
| 1738 | return opterror(opt, "must point to a commit", 0)(opterror((opt),("must point to a commit"),(0)), const_error( )); |
| 1739 | |
| 1740 | return 0; |
| 1741 | } |