| #include "builtin.h" | 
 | #include "cache.h" | 
 | #include "parse-options.h" | 
 | #include "refs.h" | 
 | #include "wildmatch.h" | 
 | #include "commit.h" | 
 | #include "remote.h" | 
 | #include "color.h" | 
 | #include "tag.h" | 
 | #include "quote.h" | 
 | #include "ref-filter.h" | 
 | #include "revision.h" | 
 | #include "utf8.h" | 
 | #include "git-compat-util.h" | 
 | #include "version.h" | 
 |  | 
 | 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", FIELD_STR, objectname_atom_parser }, | 
 | 	{ "tree" }, | 
 | 	{ "parent" }, | 
 | 	{ "numparent", FIELD_ULONG }, | 
 | 	{ "object" }, | 
 | 	{ "type" }, | 
 | 	{ "tag" }, | 
 | 	{ "author" }, | 
 | 	{ "authorname" }, | 
 | 	{ "authoremail" }, | 
 | 	{ "authordate", FIELD_TIME }, | 
 | 	{ "committer" }, | 
 | 	{ "committername" }, | 
 | 	{ "committeremail" }, | 
 | 	{ "committerdate", FIELD_TIME }, | 
 | 	{ "tagger" }, | 
 | 	{ "taggername" }, | 
 | 	{ "taggeremail" }, | 
 | 	{ "taggerdate", FIELD_TIME }, | 
 | 	{ "creator" }, | 
 | 	{ "creatordate", FIELD_TIME }, | 
 | 	{ "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", FIELD_STR, color_atom_parser }, | 
 | 	{ "align", FIELD_STR, align_atom_parser }, | 
 | 	{ "end" }, | 
 | }; | 
 |  | 
 | #define REF_FORMATTING_STATE_INIT  { 0, NULL } | 
 |  | 
 | struct ref_formatting_stack { | 
 | 	struct ref_formatting_stack *prev; | 
 | 	struct strbuf output; | 
 | 	void (*at_end)(struct ref_formatting_stack *stack); | 
 | 	void *at_end_data; | 
 | }; | 
 |  | 
 | struct ref_formatting_state { | 
 | 	int quote_style; | 
 | 	struct ref_formatting_stack *stack; | 
 | }; | 
 |  | 
 | struct atom_value { | 
 | 	const char *s; | 
 | 	union { | 
 | 		struct align align; | 
 | 	} u; | 
 | 	void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state); | 
 | 	unsigned long ul; /* used for sorting when not FIELD_STR */ | 
 | }; | 
 |  | 
 | /* | 
 |  * 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, atom_len; | 
 |  | 
 | 	sp = atom; | 
 | 	if (*sp == '*' && sp < ep) | 
 | 		sp++; /* deref */ | 
 | 	if (ep <= sp) | 
 | 		die(_("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 | 
 | 	 * shouldn't be used for checking against the valid_atom | 
 | 	 * table. | 
 | 	 */ | 
 | 	arg = memchr(sp, ':', ep - sp); | 
 | 	atom_len = (arg ? arg : ep) - sp; | 
 |  | 
 | 	/* Is the atom a valid one? */ | 
 | 	for (i = 0; i < ARRAY_SIZE(valid_atom); i++) { | 
 | 		int len = strlen(valid_atom[i].name); | 
 | 		if (len == atom_len && !memcmp(valid_atom[i].name, sp, len)) | 
 | 			break; | 
 | 	} | 
 |  | 
 | 	if (ARRAY_SIZE(valid_atom) <= i) | 
 | 		die(_("unknown field name: %.*s"), (int)(ep-atom), atom); | 
 |  | 
 | 	/* Add it in, including the deref prefix */ | 
 | 	at = used_atom_cnt; | 
 | 	used_atom_cnt++; | 
 | 	REALLOC_ARRAY(used_atom, used_atom_cnt); | 
 | 	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].name, "symref")) | 
 | 		need_symref = 1; | 
 | 	return at; | 
 | } | 
 |  | 
 | static void quote_formatting(struct strbuf *s, const char *str, int quote_style) | 
 | { | 
 | 	switch (quote_style) { | 
 | 	case QUOTE_NONE: | 
 | 		strbuf_addstr(s, str); | 
 | 		break; | 
 | 	case QUOTE_SHELL: | 
 | 		sq_quote_buf(s, str); | 
 | 		break; | 
 | 	case QUOTE_PERL: | 
 | 		perl_quote_buf(s, str); | 
 | 		break; | 
 | 	case QUOTE_PYTHON: | 
 | 		python_quote_buf(s, str); | 
 | 		break; | 
 | 	case QUOTE_TCL: | 
 | 		tcl_quote_buf(s, str); | 
 | 		break; | 
 | 	} | 
 | } | 
 |  | 
 | static void append_atom(struct atom_value *v, struct ref_formatting_state *state) | 
 | { | 
 | 	/* | 
 | 	 * Quote formatting is only done when the stack has a single | 
 | 	 * element. Otherwise quote formatting is done on the | 
 | 	 * element's entire output strbuf when the %(end) atom is | 
 | 	 * encountered. | 
 | 	 */ | 
 | 	if (!state->stack->prev) | 
 | 		quote_formatting(&state->stack->output, v->s, state->quote_style); | 
 | 	else | 
 | 		strbuf_addstr(&state->stack->output, v->s); | 
 | } | 
 |  | 
 | static void push_stack_element(struct ref_formatting_stack **stack) | 
 | { | 
 | 	struct ref_formatting_stack *s = xcalloc(1, sizeof(struct ref_formatting_stack)); | 
 |  | 
 | 	strbuf_init(&s->output, 0); | 
 | 	s->prev = *stack; | 
 | 	*stack = s; | 
 | } | 
 |  | 
 | static void pop_stack_element(struct ref_formatting_stack **stack) | 
 | { | 
 | 	struct ref_formatting_stack *current = *stack; | 
 | 	struct ref_formatting_stack *prev = current->prev; | 
 |  | 
 | 	if (prev) | 
 | 		strbuf_addbuf(&prev->output, ¤t->output); | 
 | 	strbuf_release(¤t->output); | 
 | 	free(current); | 
 | 	*stack = prev; | 
 | } | 
 |  | 
 | static void end_align_handler(struct ref_formatting_stack *stack) | 
 | { | 
 | 	struct align *align = (struct align *)stack->at_end_data; | 
 | 	struct strbuf s = STRBUF_INIT; | 
 |  | 
 | 	strbuf_utf8_align(&s, align->position, align->width, stack->output.buf); | 
 | 	strbuf_swap(&stack->output, &s); | 
 | 	strbuf_release(&s); | 
 | } | 
 |  | 
 | static void align_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state) | 
 | { | 
 | 	struct ref_formatting_stack *new; | 
 |  | 
 | 	push_stack_element(&state->stack); | 
 | 	new = state->stack; | 
 | 	new->at_end = end_align_handler; | 
 | 	new->at_end_data = &atomv->u.align; | 
 | } | 
 |  | 
 | static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state) | 
 | { | 
 | 	struct ref_formatting_stack *current = state->stack; | 
 | 	struct strbuf s = STRBUF_INIT; | 
 |  | 
 | 	if (!current->at_end) | 
 | 		die(_("format: %%(end) atom used without corresponding atom")); | 
 | 	current->at_end(current); | 
 |  | 
 | 	/* | 
 | 	 * Perform quote formatting when the stack element is that of | 
 | 	 * a supporting atom. If nested then perform quote formatting | 
 | 	 * only on the topmost supporting atom. | 
 | 	 */ | 
 | 	if (!state->stack->prev->prev) { | 
 | 		quote_formatting(&s, current->output.buf, state->quote_style); | 
 | 		strbuf_swap(¤t->output, &s); | 
 | 	} | 
 | 	strbuf_release(&s); | 
 | 	pop_stack_element(&state->stack); | 
 | } | 
 |  | 
 | /* | 
 |  * In a format string, find the next occurrence of %(atom). | 
 |  */ | 
 | static const char *find_next(const char *cp) | 
 | { | 
 | 	while (*cp) { | 
 | 		if (*cp == '%') { | 
 | 			/* | 
 | 			 * %( is the start of an atom; | 
 | 			 * %% is a quoted per-cent. | 
 | 			 */ | 
 | 			if (cp[1] == '(') | 
 | 				return cp; | 
 | 			else if (cp[1] == '%') | 
 | 				cp++; /* skip over two % */ | 
 | 			/* otherwise this is a singleton, literal % */ | 
 | 		} | 
 | 		cp++; | 
 | 	} | 
 | 	return NULL; | 
 | } | 
 |  | 
 | /* | 
 |  * Make sure the format string is well formed, and parse out | 
 |  * the used atoms. | 
 |  */ | 
 | int verify_ref_format(const char *format) | 
 | { | 
 | 	const char *cp, *sp; | 
 |  | 
 | 	need_color_reset_at_eol = 0; | 
 | 	for (cp = format; *cp && (sp = find_next(cp)); ) { | 
 | 		const char *color, *ep = strchr(sp, ')'); | 
 | 		int at; | 
 |  | 
 | 		if (!ep) | 
 | 			return error(_("malformed format string %s"), sp); | 
 | 		/* sp points at "%(" and ep points at the closing ")" */ | 
 | 		at = parse_ref_filter_atom(sp + 2, ep); | 
 | 		cp = ep + 1; | 
 |  | 
 | 		if (skip_prefix(used_atom[at].name, "color:", &color)) | 
 | 			need_color_reset_at_eol = !!strcmp(color, "reset"); | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* | 
 |  * Given an object name, read the object data and size, and return a | 
 |  * "struct object".  If the object data we are returning is also borrowed | 
 |  * by the "struct object" representation, set *eaten as well---it is a | 
 |  * signal from parse_object_buffer to us not to free the buffer. | 
 |  */ | 
 | static void *get_obj(const unsigned char *sha1, struct object **obj, unsigned long *sz, int *eaten) | 
 | { | 
 | 	enum object_type type; | 
 | 	void *buf = read_sha1_file(sha1, &type, sz); | 
 |  | 
 | 	if (buf) | 
 | 		*obj = parse_object_buffer(sha1, type, *sz, buf, eaten); | 
 | 	else | 
 | 		*obj = NULL; | 
 | 	return buf; | 
 | } | 
 |  | 
 | static int grab_objectname(const char *name, const unsigned char *sha1, | 
 | 			   struct atom_value *v, struct used_atom *atom) | 
 | { | 
 | 	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; | 
 | } | 
 |  | 
 | /* See grab_values */ | 
 | static void grab_common_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < used_atom_cnt; i++) { | 
 | 		const char *name = used_atom[i].name; | 
 | 		struct atom_value *v = &val[i]; | 
 | 		if (!!deref != (*name == '*')) | 
 | 			continue; | 
 | 		if (deref) | 
 | 			name++; | 
 | 		if (!strcmp(name, "objecttype")) | 
 | 			v->s = typename(obj->type); | 
 | 		else if (!strcmp(name, "objectsize")) { | 
 | 			v->ul = sz; | 
 | 			v->s = xstrfmt("%lu", sz); | 
 | 		} | 
 | 		else if (deref) | 
 | 			grab_objectname(name, obj->oid.hash, v, &used_atom[i]); | 
 | 	} | 
 | } | 
 |  | 
 | /* See grab_values */ | 
 | static void grab_tag_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz) | 
 | { | 
 | 	int i; | 
 | 	struct tag *tag = (struct tag *) obj; | 
 |  | 
 | 	for (i = 0; i < used_atom_cnt; i++) { | 
 | 		const char *name = used_atom[i].name; | 
 | 		struct atom_value *v = &val[i]; | 
 | 		if (!!deref != (*name == '*')) | 
 | 			continue; | 
 | 		if (deref) | 
 | 			name++; | 
 | 		if (!strcmp(name, "tag")) | 
 | 			v->s = tag->tag; | 
 | 		else if (!strcmp(name, "type") && tag->tagged) | 
 | 			v->s = typename(tag->tagged->type); | 
 | 		else if (!strcmp(name, "object") && tag->tagged) | 
 | 			v->s = xstrdup(oid_to_hex(&tag->tagged->oid)); | 
 | 	} | 
 | } | 
 |  | 
 | /* See grab_values */ | 
 | static void grab_commit_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz) | 
 | { | 
 | 	int i; | 
 | 	struct commit *commit = (struct commit *) obj; | 
 |  | 
 | 	for (i = 0; i < used_atom_cnt; i++) { | 
 | 		const char *name = used_atom[i].name; | 
 | 		struct atom_value *v = &val[i]; | 
 | 		if (!!deref != (*name == '*')) | 
 | 			continue; | 
 | 		if (deref) | 
 | 			name++; | 
 | 		if (!strcmp(name, "tree")) { | 
 | 			v->s = xstrdup(oid_to_hex(&commit->tree->object.oid)); | 
 | 		} | 
 | 		else if (!strcmp(name, "numparent")) { | 
 | 			v->ul = commit_list_count(commit->parents); | 
 | 			v->s = xstrfmt("%lu", v->ul); | 
 | 		} | 
 | 		else if (!strcmp(name, "parent")) { | 
 | 			struct commit_list *parents; | 
 | 			struct strbuf s = STRBUF_INIT; | 
 | 			for (parents = commit->parents; parents; parents = parents->next) { | 
 | 				struct commit *parent = parents->item; | 
 | 				if (parents != commit->parents) | 
 | 					strbuf_addch(&s, ' '); | 
 | 				strbuf_addstr(&s, oid_to_hex(&parent->object.oid)); | 
 | 			} | 
 | 			v->s = strbuf_detach(&s, NULL); | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | static const char *find_wholine(const char *who, int wholen, const char *buf, unsigned long sz) | 
 | { | 
 | 	const char *eol; | 
 | 	while (*buf) { | 
 | 		if (!strncmp(buf, who, wholen) && | 
 | 		    buf[wholen] == ' ') | 
 | 			return buf + wholen + 1; | 
 | 		eol = strchr(buf, '\n'); | 
 | 		if (!eol) | 
 | 			return ""; | 
 | 		eol++; | 
 | 		if (*eol == '\n') | 
 | 			return ""; /* end of header */ | 
 | 		buf = eol; | 
 | 	} | 
 | 	return ""; | 
 | } | 
 |  | 
 | static const char *copy_line(const char *buf) | 
 | { | 
 | 	const char *eol = strchrnul(buf, '\n'); | 
 | 	return xmemdupz(buf, eol - buf); | 
 | } | 
 |  | 
 | static const char *copy_name(const char *buf) | 
 | { | 
 | 	const char *cp; | 
 | 	for (cp = buf; *cp && *cp != '\n'; cp++) { | 
 | 		if (!strncmp(cp, " <", 2)) | 
 | 			return xmemdupz(buf, cp - buf); | 
 | 	} | 
 | 	return ""; | 
 | } | 
 |  | 
 | static const char *copy_email(const char *buf) | 
 | { | 
 | 	const char *email = strchr(buf, '<'); | 
 | 	const char *eoemail; | 
 | 	if (!email) | 
 | 		return ""; | 
 | 	eoemail = strchr(email, '>'); | 
 | 	if (!eoemail) | 
 | 		return ""; | 
 | 	return xmemdupz(email, eoemail + 1 - email); | 
 | } | 
 |  | 
 | static char *copy_subject(const char *buf, unsigned long len) | 
 | { | 
 | 	char *r = xmemdupz(buf, len); | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < len; i++) | 
 | 		if (r[i] == '\n') | 
 | 			r[i] = ' '; | 
 |  | 
 | 	return r; | 
 | } | 
 |  | 
 | static void grab_date(const char *buf, struct atom_value *v, const char *atomname) | 
 | { | 
 | 	const char *eoemail = strstr(buf, "> "); | 
 | 	char *zone; | 
 | 	unsigned long timestamp; | 
 | 	long tz; | 
 | 	struct date_mode date_mode = { DATE_NORMAL }; | 
 | 	const char *formatp; | 
 |  | 
 | 	/* | 
 | 	 * We got here because atomname ends in "date" or "date<something>"; | 
 | 	 * it's not possible that <something> is not ":<format>" because | 
 | 	 * parse_ref_filter_atom() wouldn't have allowed it, so we can assume that no | 
 | 	 * ":" means no format is specified, and use the default. | 
 | 	 */ | 
 | 	formatp = strchr(atomname, ':'); | 
 | 	if (formatp != NULL) { | 
 | 		formatp++; | 
 | 		parse_date_format(formatp, &date_mode); | 
 | 	} | 
 |  | 
 | 	if (!eoemail) | 
 | 		goto bad; | 
 | 	timestamp = strtoul(eoemail + 2, &zone, 10); | 
 | 	if (timestamp == ULONG_MAX) | 
 | 		goto bad; | 
 | 	tz = strtol(zone, NULL, 10); | 
 | 	if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE) | 
 | 		goto bad; | 
 | 	v->s = xstrdup(show_date(timestamp, tz, &date_mode)); | 
 | 	v->ul = timestamp; | 
 | 	return; | 
 |  bad: | 
 | 	v->s = ""; | 
 | 	v->ul = 0; | 
 | } | 
 |  | 
 | /* See grab_values */ | 
 | static void grab_person(const char *who, struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz) | 
 | { | 
 | 	int i; | 
 | 	int wholen = strlen(who); | 
 | 	const char *wholine = NULL; | 
 |  | 
 | 	for (i = 0; i < used_atom_cnt; i++) { | 
 | 		const char *name = used_atom[i].name; | 
 | 		struct atom_value *v = &val[i]; | 
 | 		if (!!deref != (*name == '*')) | 
 | 			continue; | 
 | 		if (deref) | 
 | 			name++; | 
 | 		if (strncmp(who, name, wholen)) | 
 | 			continue; | 
 | 		if (name[wholen] != 0 && | 
 | 		    strcmp(name + wholen, "name") && | 
 | 		    strcmp(name + wholen, "email") && | 
 | 		    !starts_with(name + wholen, "date")) | 
 | 			continue; | 
 | 		if (!wholine) | 
 | 			wholine = find_wholine(who, wholen, buf, sz); | 
 | 		if (!wholine) | 
 | 			return; /* no point looking for it */ | 
 | 		if (name[wholen] == 0) | 
 | 			v->s = copy_line(wholine); | 
 | 		else if (!strcmp(name + wholen, "name")) | 
 | 			v->s = copy_name(wholine); | 
 | 		else if (!strcmp(name + wholen, "email")) | 
 | 			v->s = copy_email(wholine); | 
 | 		else if (starts_with(name + wholen, "date")) | 
 | 			grab_date(wholine, v, name); | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * For a tag or a commit object, if "creator" or "creatordate" is | 
 | 	 * requested, do something special. | 
 | 	 */ | 
 | 	if (strcmp(who, "tagger") && strcmp(who, "committer")) | 
 | 		return; /* "author" for commit object is not wanted */ | 
 | 	if (!wholine) | 
 | 		wholine = find_wholine(who, wholen, buf, sz); | 
 | 	if (!wholine) | 
 | 		return; | 
 | 	for (i = 0; i < used_atom_cnt; i++) { | 
 | 		const char *name = used_atom[i].name; | 
 | 		struct atom_value *v = &val[i]; | 
 | 		if (!!deref != (*name == '*')) | 
 | 			continue; | 
 | 		if (deref) | 
 | 			name++; | 
 |  | 
 | 		if (starts_with(name, "creatordate")) | 
 | 			grab_date(wholine, v, name); | 
 | 		else if (!strcmp(name, "creator")) | 
 | 			v->s = copy_line(wholine); | 
 | 	} | 
 | } | 
 |  | 
 | static void find_subpos(const char *buf, unsigned long sz, | 
 | 			const char **sub, unsigned long *sublen, | 
 | 			const char **body, unsigned long *bodylen, | 
 | 			unsigned long *nonsiglen, | 
 | 			const char **sig, unsigned long *siglen) | 
 | { | 
 | 	const char *eol; | 
 | 	/* skip past header until we hit empty line */ | 
 | 	while (*buf && *buf != '\n') { | 
 | 		eol = strchrnul(buf, '\n'); | 
 | 		if (*eol) | 
 | 			eol++; | 
 | 		buf = eol; | 
 | 	} | 
 | 	/* skip any empty lines */ | 
 | 	while (*buf == '\n') | 
 | 		buf++; | 
 |  | 
 | 	/* parse signature first; we might not even have a subject line */ | 
 | 	*sig = buf + parse_signature(buf, strlen(buf)); | 
 | 	*siglen = strlen(*sig); | 
 |  | 
 | 	/* subject is first non-empty line */ | 
 | 	*sub = buf; | 
 | 	/* subject goes to first empty line */ | 
 | 	while (buf < *sig && *buf && *buf != '\n') { | 
 | 		eol = strchrnul(buf, '\n'); | 
 | 		if (*eol) | 
 | 			eol++; | 
 | 		buf = eol; | 
 | 	} | 
 | 	*sublen = buf - *sub; | 
 | 	/* drop trailing newline, if present */ | 
 | 	if (*sublen && (*sub)[*sublen - 1] == '\n') | 
 | 		*sublen -= 1; | 
 |  | 
 | 	/* skip any empty lines */ | 
 | 	while (*buf == '\n') | 
 | 		buf++; | 
 | 	*body = buf; | 
 | 	*bodylen = strlen(buf); | 
 | 	*nonsiglen = *sig - buf; | 
 | } | 
 |  | 
 | /* | 
 |  * If 'lines' is greater than 0, append that many lines from the given | 
 |  * 'buf' of length 'size' to the given strbuf. | 
 |  */ | 
 | static void append_lines(struct strbuf *out, const char *buf, unsigned long size, int lines) | 
 | { | 
 | 	int i; | 
 | 	const char *sp, *eol; | 
 | 	size_t len; | 
 |  | 
 | 	sp = buf; | 
 |  | 
 | 	for (i = 0; i < lines && sp < buf + size; i++) { | 
 | 		if (i) | 
 | 			strbuf_addstr(out, "\n    "); | 
 | 		eol = memchr(sp, '\n', size - (sp - buf)); | 
 | 		len = eol ? eol - sp : size - (sp - buf); | 
 | 		strbuf_add(out, sp, len); | 
 | 		if (!eol) | 
 | 			break; | 
 | 		sp = eol + 1; | 
 | 	} | 
 | } | 
 |  | 
 | /* See grab_values */ | 
 | static void grab_sub_body_contents(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz) | 
 | { | 
 | 	int i; | 
 | 	const char *subpos = NULL, *bodypos = NULL, *sigpos = NULL; | 
 | 	unsigned long sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0; | 
 |  | 
 | 	for (i = 0; i < used_atom_cnt; i++) { | 
 | 		struct used_atom *atom = &used_atom[i]; | 
 | 		const char *name = atom->name; | 
 | 		struct atom_value *v = &val[i]; | 
 | 		if (!!deref != (*name == '*')) | 
 | 			continue; | 
 | 		if (deref) | 
 | 			name++; | 
 | 		if (strcmp(name, "subject") && | 
 | 		    strcmp(name, "body") && | 
 | 		    !starts_with(name, "contents")) | 
 | 			continue; | 
 | 		if (!subpos) | 
 | 			find_subpos(buf, sz, | 
 | 				    &subpos, &sublen, | 
 | 				    &bodypos, &bodylen, &nonsiglen, | 
 | 				    &sigpos, &siglen); | 
 |  | 
 | 		if (atom->u.contents.option == C_SUB) | 
 | 			v->s = copy_subject(subpos, sublen); | 
 | 		else if (atom->u.contents.option == C_BODY_DEP) | 
 | 			v->s = xmemdupz(bodypos, bodylen); | 
 | 		else if (atom->u.contents.option == C_BODY) | 
 | 			v->s = xmemdupz(bodypos, nonsiglen); | 
 | 		else if (atom->u.contents.option == C_SIG) | 
 | 			v->s = xmemdupz(sigpos, siglen); | 
 | 		else if (atom->u.contents.option == C_LINES) { | 
 | 			struct strbuf s = STRBUF_INIT; | 
 | 			const char *contents_end = bodylen + bodypos - siglen; | 
 |  | 
 | 			/*  Size is the length of the message after removing the signature */ | 
 | 			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); | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 |  * We want to have empty print-string for field requests | 
 |  * that do not apply (e.g. "authordate" for a tag object) | 
 |  */ | 
 | static void fill_missing_values(struct atom_value *val) | 
 | { | 
 | 	int i; | 
 | 	for (i = 0; i < used_atom_cnt; i++) { | 
 | 		struct atom_value *v = &val[i]; | 
 | 		if (v->s == NULL) | 
 | 			v->s = ""; | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 |  * val is a list of atom_value to hold returned values.  Extract | 
 |  * the values for atoms in used_atom array out of (obj, buf, sz). | 
 |  * when deref is false, (obj, buf, sz) is the object that is | 
 |  * pointed at by the ref itself; otherwise it is the object the | 
 |  * ref (which is a tag) refers to. | 
 |  */ | 
 | static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz) | 
 | { | 
 | 	grab_common_values(val, deref, obj, buf, sz); | 
 | 	switch (obj->type) { | 
 | 	case OBJ_TAG: | 
 | 		grab_tag_values(val, deref, obj, buf, sz); | 
 | 		grab_sub_body_contents(val, deref, obj, buf, sz); | 
 | 		grab_person("tagger", val, deref, obj, buf, sz); | 
 | 		break; | 
 | 	case OBJ_COMMIT: | 
 | 		grab_commit_values(val, deref, obj, buf, sz); | 
 | 		grab_sub_body_contents(val, deref, obj, buf, sz); | 
 | 		grab_person("author", val, deref, obj, buf, sz); | 
 | 		grab_person("committer", val, deref, obj, buf, sz); | 
 | 		break; | 
 | 	case OBJ_TREE: | 
 | 		/* grab_tree_values(val, deref, obj, buf, sz); */ | 
 | 		break; | 
 | 	case OBJ_BLOB: | 
 | 		/* grab_blob_values(val, deref, obj, buf, sz); */ | 
 | 		break; | 
 | 	default: | 
 | 		die("Eh?  Object of type %d?", obj->type); | 
 | 	} | 
 | } | 
 |  | 
 | static inline char *copy_advance(char *dst, const char *src) | 
 | { | 
 | 	while (*src) | 
 | 		*dst++ = *src++; | 
 | 	return dst; | 
 | } | 
 |  | 
 | static const char *strip_ref_components(const char *refname, const char *nr_arg) | 
 | { | 
 | 	char *end; | 
 | 	long nr = strtol(nr_arg, &end, 10); | 
 | 	long remaining = nr; | 
 | 	const char *start = refname; | 
 |  | 
 | 	if (nr < 1 || *end != '\0') | 
 | 		die(_(":strip= requires a positive integer argument")); | 
 |  | 
 | 	while (remaining) { | 
 | 		switch (*start++) { | 
 | 		case '\0': | 
 | 			die(_("ref '%s' does not have %ld components to :strip"), | 
 | 			    refname, nr); | 
 | 		case '/': | 
 | 			remaining--; | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	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. | 
 |  */ | 
 | static void populate_value(struct ref_array_item *ref) | 
 | { | 
 | 	void *buf; | 
 | 	struct object *obj; | 
 | 	int eaten, i; | 
 | 	unsigned long size; | 
 | 	const unsigned char *tagged; | 
 |  | 
 | 	ref->value = xcalloc(used_atom_cnt, sizeof(struct atom_value)); | 
 |  | 
 | 	if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) { | 
 | 		unsigned char unused1[20]; | 
 | 		ref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING, | 
 | 					     unused1, NULL); | 
 | 		if (!ref->symref) | 
 | 			ref->symref = ""; | 
 | 	} | 
 |  | 
 | 	/* Fill in specials first */ | 
 | 	for (i = 0; i < used_atom_cnt; 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; | 
 | 		struct branch *branch = NULL; | 
 |  | 
 | 		v->handler = append_atom; | 
 |  | 
 | 		if (*name == '*') { | 
 | 			deref = 1; | 
 | 			name++; | 
 | 		} | 
 |  | 
 | 		if (starts_with(name, "refname")) | 
 | 			refname = ref->refname; | 
 | 		else if (starts_with(name, "symref")) | 
 | 			refname = ref->symref ? ref->symref : ""; | 
 | 		else if (starts_with(name, "upstream")) { | 
 | 			const char *branch_name; | 
 | 			/* only local branches may have an upstream */ | 
 | 			if (!skip_prefix(ref->refname, "refs/heads/", | 
 | 					 &branch_name)) | 
 | 				continue; | 
 | 			branch = branch_get(branch_name); | 
 |  | 
 | 			refname = branch_get_upstream(branch, NULL); | 
 | 			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/", | 
 | 					 &branch_name)) | 
 | 				continue; | 
 | 			branch = branch_get(branch_name); | 
 |  | 
 | 			refname = branch_get_push(branch, NULL); | 
 | 			if (!refname) | 
 | 				continue; | 
 | 			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; | 
 | 			if (ref->flag & REF_ISSYMREF) | 
 | 				cp = copy_advance(cp, ",symref"); | 
 | 			if (ref->flag & REF_ISPACKED) | 
 | 				cp = copy_advance(cp, ",packed"); | 
 | 			if (cp == buf) | 
 | 				v->s = ""; | 
 | 			else { | 
 | 				*cp = '\0'; | 
 | 				v->s = xstrdup(buf + 1); | 
 | 			} | 
 | 			continue; | 
 | 		} else if (!deref && grab_objectname(name, ref->objectname, v, atom)) { | 
 | 			continue; | 
 | 		} else if (!strcmp(name, "HEAD")) { | 
 | 			const char *head; | 
 | 			unsigned char sha1[20]; | 
 |  | 
 | 			head = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, | 
 | 						  sha1, NULL); | 
 | 			if (!strcmp(ref->refname, head)) | 
 | 				v->s = "*"; | 
 | 			else | 
 | 				v->s = " "; | 
 | 			continue; | 
 | 		} else if (starts_with(name, "align")) { | 
 | 			v->u.align = atom->u.align; | 
 | 			v->handler = align_atom_handler; | 
 | 			continue; | 
 | 		} else if (!strcmp(name, "end")) { | 
 | 			v->handler = end_atom_handler; | 
 | 			continue; | 
 | 		} else | 
 | 			continue; | 
 |  | 
 | 		formatp = strchr(name, ':'); | 
 | 		if (formatp) { | 
 | 			const char *arg; | 
 |  | 
 | 			formatp++; | 
 | 			if (!strcmp(formatp, "short")) | 
 | 				refname = shorten_unambiguous_ref(refname, | 
 | 						      warn_ambiguous_refs); | 
 | 			else if (skip_prefix(formatp, "strip=", &arg)) | 
 | 				refname = strip_ref_components(refname, arg); | 
 | 			else | 
 | 				die(_("unknown %.*s format %s"), | 
 | 				    (int)(formatp - name), name, formatp); | 
 | 		} | 
 |  | 
 | 		if (!deref) | 
 | 			v->s = refname; | 
 | 		else | 
 | 			v->s = xstrfmt("%s^{}", refname); | 
 | 	} | 
 |  | 
 | 	for (i = 0; i < used_atom_cnt; i++) { | 
 | 		struct atom_value *v = &ref->value[i]; | 
 | 		if (v->s == NULL) | 
 | 			goto need_obj; | 
 | 	} | 
 | 	return; | 
 |  | 
 |  need_obj: | 
 | 	buf = get_obj(ref->objectname, &obj, &size, &eaten); | 
 | 	if (!buf) | 
 | 		die(_("missing object %s for %s"), | 
 | 		    sha1_to_hex(ref->objectname), ref->refname); | 
 | 	if (!obj) | 
 | 		die(_("parse_object_buffer failed on %s for %s"), | 
 | 		    sha1_to_hex(ref->objectname), ref->refname); | 
 |  | 
 | 	grab_values(ref->value, 0, obj, buf, size); | 
 | 	if (!eaten) | 
 | 		free(buf); | 
 |  | 
 | 	/* | 
 | 	 * If there is no atom that wants to know about tagged | 
 | 	 * object, we are done. | 
 | 	 */ | 
 | 	if (!need_tagged || (obj->type != OBJ_TAG)) | 
 | 		return; | 
 |  | 
 | 	/* | 
 | 	 * If it is a tag object, see if we use a value that derefs | 
 | 	 * the object, and if we do grab the object it refers to. | 
 | 	 */ | 
 | 	tagged = ((struct tag *)obj)->tagged->oid.hash; | 
 |  | 
 | 	/* | 
 | 	 * NEEDSWORK: This derefs tag only once, which | 
 | 	 * is good to deal with chains of trust, but | 
 | 	 * is not consistent with what deref_tag() does | 
 | 	 * which peels the onion to the core. | 
 | 	 */ | 
 | 	buf = get_obj(tagged, &obj, &size, &eaten); | 
 | 	if (!buf) | 
 | 		die(_("missing object %s for %s"), | 
 | 		    sha1_to_hex(tagged), ref->refname); | 
 | 	if (!obj) | 
 | 		die(_("parse_object_buffer failed on %s for %s"), | 
 | 		    sha1_to_hex(tagged), ref->refname); | 
 | 	grab_values(ref->value, 1, obj, buf, size); | 
 | 	if (!eaten) | 
 | 		free(buf); | 
 | } | 
 |  | 
 | /* | 
 |  * Given a ref, return the value for the atom.  This lazily gets value | 
 |  * out of the object by calling populate value. | 
 |  */ | 
 | static void get_ref_atom_value(struct ref_array_item *ref, int atom, struct atom_value **v) | 
 | { | 
 | 	if (!ref->value) { | 
 | 		populate_value(ref); | 
 | 		fill_missing_values(ref->value); | 
 | 	} | 
 | 	*v = &ref->value[atom]; | 
 | } | 
 |  | 
 | enum contains_result { | 
 | 	CONTAINS_UNKNOWN = -1, | 
 | 	CONTAINS_NO = 0, | 
 | 	CONTAINS_YES = 1 | 
 | }; | 
 |  | 
 | /* | 
 |  * Mimicking the real stack, this stack lives on the heap, avoiding stack | 
 |  * overflows. | 
 |  * | 
 |  * At each recursion step, the stack items points to the commits whose | 
 |  * ancestors are to be inspected. | 
 |  */ | 
 | struct contains_stack { | 
 | 	int nr, alloc; | 
 | 	struct contains_stack_entry { | 
 | 		struct commit *commit; | 
 | 		struct commit_list *parents; | 
 | 	} *contains_stack; | 
 | }; | 
 |  | 
 | static int in_commit_list(const struct commit_list *want, struct commit *c) | 
 | { | 
 | 	for (; want; want = want->next) | 
 | 		if (!oidcmp(&want->item->object.oid, &c->object.oid)) | 
 | 			return 1; | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* | 
 |  * Test whether the candidate or one of its parents is contained in the list. | 
 |  * Do not recurse to find out, though, but return -1 if inconclusive. | 
 |  */ | 
 | static enum contains_result contains_test(struct commit *candidate, | 
 | 			    const struct commit_list *want) | 
 | { | 
 | 	/* was it previously marked as containing a want commit? */ | 
 | 	if (candidate->object.flags & TMP_MARK) | 
 | 		return 1; | 
 | 	/* or marked as not possibly containing a want commit? */ | 
 | 	if (candidate->object.flags & UNINTERESTING) | 
 | 		return 0; | 
 | 	/* or are we it? */ | 
 | 	if (in_commit_list(want, candidate)) { | 
 | 		candidate->object.flags |= TMP_MARK; | 
 | 		return 1; | 
 | 	} | 
 |  | 
 | 	if (parse_commit(candidate) < 0) | 
 | 		return 0; | 
 |  | 
 | 	return -1; | 
 | } | 
 |  | 
 | static void push_to_contains_stack(struct commit *candidate, struct contains_stack *contains_stack) | 
 | { | 
 | 	ALLOC_GROW(contains_stack->contains_stack, contains_stack->nr + 1, contains_stack->alloc); | 
 | 	contains_stack->contains_stack[contains_stack->nr].commit = candidate; | 
 | 	contains_stack->contains_stack[contains_stack->nr++].parents = candidate->parents; | 
 | } | 
 |  | 
 | static enum contains_result contains_tag_algo(struct commit *candidate, | 
 | 		const struct commit_list *want) | 
 | { | 
 | 	struct contains_stack contains_stack = { 0, 0, NULL }; | 
 | 	int result = contains_test(candidate, want); | 
 |  | 
 | 	if (result != CONTAINS_UNKNOWN) | 
 | 		return result; | 
 |  | 
 | 	push_to_contains_stack(candidate, &contains_stack); | 
 | 	while (contains_stack.nr) { | 
 | 		struct contains_stack_entry *entry = &contains_stack.contains_stack[contains_stack.nr - 1]; | 
 | 		struct commit *commit = entry->commit; | 
 | 		struct commit_list *parents = entry->parents; | 
 |  | 
 | 		if (!parents) { | 
 | 			commit->object.flags |= UNINTERESTING; | 
 | 			contains_stack.nr--; | 
 | 		} | 
 | 		/* | 
 | 		 * If we just popped the stack, parents->item has been marked, | 
 | 		 * therefore contains_test will return a meaningful 0 or 1. | 
 | 		 */ | 
 | 		else switch (contains_test(parents->item, want)) { | 
 | 		case CONTAINS_YES: | 
 | 			commit->object.flags |= TMP_MARK; | 
 | 			contains_stack.nr--; | 
 | 			break; | 
 | 		case CONTAINS_NO: | 
 | 			entry->parents = parents->next; | 
 | 			break; | 
 | 		case CONTAINS_UNKNOWN: | 
 | 			push_to_contains_stack(parents->item, &contains_stack); | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	free(contains_stack.contains_stack); | 
 | 	return contains_test(candidate, want); | 
 | } | 
 |  | 
 | static int commit_contains(struct ref_filter *filter, struct commit *commit) | 
 | { | 
 | 	if (filter->with_commit_tag_algo) | 
 | 		return contains_tag_algo(commit, filter->with_commit); | 
 | 	return is_descendant_of(commit, filter->with_commit); | 
 | } | 
 |  | 
 | /* | 
 |  * Return 1 if the refname matches one of the patterns, otherwise 0. | 
 |  * A pattern can be a literal prefix (e.g. a refname "refs/heads/master" | 
 |  * matches a pattern "refs/heads/mas") or a wildcard (e.g. the same ref | 
 |  * matches "refs/heads/mas*", too). | 
 |  */ | 
 | static int match_pattern(const char **patterns, const char *refname) | 
 | { | 
 | 	/* | 
 | 	 * When no '--format' option is given we need to skip the prefix | 
 | 	 * for matching refs of tags and branches. | 
 | 	 */ | 
 | 	(void)(skip_prefix(refname, "refs/tags/", &refname) || | 
 | 	       skip_prefix(refname, "refs/heads/", &refname) || | 
 | 	       skip_prefix(refname, "refs/remotes/", &refname) || | 
 | 	       skip_prefix(refname, "refs/", &refname)); | 
 |  | 
 | 	for (; *patterns; patterns++) { | 
 | 		if (!wildmatch(*patterns, refname, 0, NULL)) | 
 | 			return 1; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* | 
 |  * Return 1 if the refname matches one of the patterns, otherwise 0. | 
 |  * A pattern can be path prefix (e.g. a refname "refs/heads/master" | 
 |  * matches a pattern "refs/heads/" but not "refs/heads/m") or a | 
 |  * wildcard (e.g. the same ref matches "refs/heads/m*", too). | 
 |  */ | 
 | static int match_name_as_path(const char **pattern, const char *refname) | 
 | { | 
 | 	int namelen = strlen(refname); | 
 | 	for (; *pattern; pattern++) { | 
 | 		const char *p = *pattern; | 
 | 		int plen = strlen(p); | 
 |  | 
 | 		if ((plen <= namelen) && | 
 | 		    !strncmp(refname, p, plen) && | 
 | 		    (refname[plen] == '\0' || | 
 | 		     refname[plen] == '/' || | 
 | 		     p[plen-1] == '/')) | 
 | 			return 1; | 
 | 		if (!wildmatch(p, refname, WM_PATHNAME, NULL)) | 
 | 			return 1; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* Return 1 if the refname matches one of the patterns, otherwise 0. */ | 
 | static int filter_pattern_match(struct ref_filter *filter, const char *refname) | 
 | { | 
 | 	if (!*filter->name_patterns) | 
 | 		return 1; /* No pattern always matches */ | 
 | 	if (filter->match_as_path) | 
 | 		return match_name_as_path(filter->name_patterns, refname); | 
 | 	return match_pattern(filter->name_patterns, refname); | 
 | } | 
 |  | 
 | /* | 
 |  * Given a ref (sha1, refname), check if the ref belongs to the array | 
 |  * of sha1s. If the given ref is a tag, check if the given tag points | 
 |  * at one of the sha1s in the given sha1 array. | 
 |  * the given sha1_array. | 
 |  * NEEDSWORK: | 
 |  * 1. Only a single level of inderection is obtained, we might want to | 
 |  * change this to account for multiple levels (e.g. annotated tags | 
 |  * pointing to annotated tags pointing to a commit.) | 
 |  * 2. As the refs are cached we might know what refname peels to without | 
 |  * the need to parse the object via parse_object(). peel_ref() might be a | 
 |  * more efficient alternative to obtain the pointee. | 
 |  */ | 
 | static const unsigned char *match_points_at(struct sha1_array *points_at, | 
 | 					    const unsigned char *sha1, | 
 | 					    const char *refname) | 
 | { | 
 | 	const unsigned char *tagged_sha1 = NULL; | 
 | 	struct object *obj; | 
 |  | 
 | 	if (sha1_array_lookup(points_at, sha1) >= 0) | 
 | 		return sha1; | 
 | 	obj = parse_object(sha1); | 
 | 	if (!obj) | 
 | 		die(_("malformed object at '%s'"), refname); | 
 | 	if (obj->type == OBJ_TAG) | 
 | 		tagged_sha1 = ((struct tag *)obj)->tagged->oid.hash; | 
 | 	if (tagged_sha1 && sha1_array_lookup(points_at, tagged_sha1) >= 0) | 
 | 		return tagged_sha1; | 
 | 	return NULL; | 
 | } | 
 |  | 
 | /* Allocate space for a new ref_array_item and copy the objectname and flag to it */ | 
 | static struct ref_array_item *new_ref_array_item(const char *refname, | 
 | 						 const unsigned char *objectname, | 
 | 						 int flag) | 
 | { | 
 | 	struct ref_array_item *ref; | 
 | 	FLEX_ALLOC_STR(ref, refname, refname); | 
 | 	hashcpy(ref->objectname, objectname); | 
 | 	ref->flag = flag; | 
 |  | 
 | 	return ref; | 
 | } | 
 |  | 
 | static int filter_ref_kind(struct ref_filter *filter, const char *refname) | 
 | { | 
 | 	unsigned int i; | 
 |  | 
 | 	static struct { | 
 | 		const char *prefix; | 
 | 		unsigned int kind; | 
 | 	} ref_kind[] = { | 
 | 		{ "refs/heads/" , FILTER_REFS_BRANCHES }, | 
 | 		{ "refs/remotes/" , FILTER_REFS_REMOTES }, | 
 | 		{ "refs/tags/", FILTER_REFS_TAGS} | 
 | 	}; | 
 |  | 
 | 	if (filter->kind == FILTER_REFS_BRANCHES || | 
 | 	    filter->kind == FILTER_REFS_REMOTES || | 
 | 	    filter->kind == FILTER_REFS_TAGS) | 
 | 		return filter->kind; | 
 | 	else if (!strcmp(refname, "HEAD")) | 
 | 		return FILTER_REFS_DETACHED_HEAD; | 
 |  | 
 | 	for (i = 0; i < ARRAY_SIZE(ref_kind); i++) { | 
 | 		if (starts_with(refname, ref_kind[i].prefix)) | 
 | 			return ref_kind[i].kind; | 
 | 	} | 
 |  | 
 | 	return FILTER_REFS_OTHERS; | 
 | } | 
 |  | 
 | /* | 
 |  * A call-back given to for_each_ref().  Filter refs and keep them for | 
 |  * later object processing. | 
 |  */ | 
 | static int ref_filter_handler(const char *refname, const struct object_id *oid, int flag, void *cb_data) | 
 | { | 
 | 	struct ref_filter_cbdata *ref_cbdata = cb_data; | 
 | 	struct ref_filter *filter = ref_cbdata->filter; | 
 | 	struct ref_array_item *ref; | 
 | 	struct commit *commit = NULL; | 
 | 	unsigned int kind; | 
 |  | 
 | 	if (flag & REF_BAD_NAME) { | 
 | 		warning(_("ignoring ref with broken name %s"), refname); | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	if (flag & REF_ISBROKEN) { | 
 | 		warning(_("ignoring broken ref %s"), refname); | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	/* Obtain the current ref kind from filter_ref_kind() and ignore unwanted refs. */ | 
 | 	kind = filter_ref_kind(filter, refname); | 
 | 	if (!(kind & filter->kind)) | 
 | 		return 0; | 
 |  | 
 | 	if (!filter_pattern_match(filter, refname)) | 
 | 		return 0; | 
 |  | 
 | 	if (filter->points_at.nr && !match_points_at(&filter->points_at, oid->hash, refname)) | 
 | 		return 0; | 
 |  | 
 | 	/* | 
 | 	 * A merge filter is applied on refs pointing to commits. Hence | 
 | 	 * obtain the commit using the 'oid' available and discard all | 
 | 	 * non-commits early. The actual filtering is done later. | 
 | 	 */ | 
 | 	if (filter->merge_commit || filter->with_commit || filter->verbose) { | 
 | 		commit = lookup_commit_reference_gently(oid->hash, 1); | 
 | 		if (!commit) | 
 | 			return 0; | 
 | 		/* We perform the filtering for the '--contains' option */ | 
 | 		if (filter->with_commit && | 
 | 		    !commit_contains(filter, commit)) | 
 | 			return 0; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * We do not open the object yet; sort may only need refname | 
 | 	 * to do its job and the resulting list may yet to be pruned | 
 | 	 * by maxcount logic. | 
 | 	 */ | 
 | 	ref = new_ref_array_item(refname, oid->hash, flag); | 
 | 	ref->commit = commit; | 
 |  | 
 | 	REALLOC_ARRAY(ref_cbdata->array->items, ref_cbdata->array->nr + 1); | 
 | 	ref_cbdata->array->items[ref_cbdata->array->nr++] = ref; | 
 | 	ref->kind = kind; | 
 | 	return 0; | 
 | } | 
 |  | 
 | /*  Free memory allocated for a ref_array_item */ | 
 | static void free_array_item(struct ref_array_item *item) | 
 | { | 
 | 	free((char *)item->symref); | 
 | 	free(item); | 
 | } | 
 |  | 
 | /* Free all memory allocated for ref_array */ | 
 | void ref_array_clear(struct ref_array *array) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < array->nr; i++) | 
 | 		free_array_item(array->items[i]); | 
 | 	free(array->items); | 
 | 	array->items = NULL; | 
 | 	array->nr = array->alloc = 0; | 
 | } | 
 |  | 
 | static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata) | 
 | { | 
 | 	struct rev_info revs; | 
 | 	int i, old_nr; | 
 | 	struct ref_filter *filter = ref_cbdata->filter; | 
 | 	struct ref_array *array = ref_cbdata->array; | 
 | 	struct commit **to_clear = xcalloc(sizeof(struct commit *), array->nr); | 
 |  | 
 | 	init_revisions(&revs, NULL); | 
 |  | 
 | 	for (i = 0; i < array->nr; i++) { | 
 | 		struct ref_array_item *item = array->items[i]; | 
 | 		add_pending_object(&revs, &item->commit->object, item->refname); | 
 | 		to_clear[i] = item->commit; | 
 | 	} | 
 |  | 
 | 	filter->merge_commit->object.flags |= UNINTERESTING; | 
 | 	add_pending_object(&revs, &filter->merge_commit->object, ""); | 
 |  | 
 | 	revs.limited = 1; | 
 | 	if (prepare_revision_walk(&revs)) | 
 | 		die(_("revision walk setup failed")); | 
 |  | 
 | 	old_nr = array->nr; | 
 | 	array->nr = 0; | 
 |  | 
 | 	for (i = 0; i < old_nr; i++) { | 
 | 		struct ref_array_item *item = array->items[i]; | 
 | 		struct commit *commit = item->commit; | 
 |  | 
 | 		int is_merged = !!(commit->object.flags & UNINTERESTING); | 
 |  | 
 | 		if (is_merged == (filter->merge == REF_FILTER_MERGED_INCLUDE)) | 
 | 			array->items[array->nr++] = array->items[i]; | 
 | 		else | 
 | 			free_array_item(item); | 
 | 	} | 
 |  | 
 | 	for (i = 0; i < old_nr; i++) | 
 | 		clear_commit_marks(to_clear[i], ALL_REV_FLAGS); | 
 | 	clear_commit_marks(filter->merge_commit, ALL_REV_FLAGS); | 
 | 	free(to_clear); | 
 | } | 
 |  | 
 | /* | 
 |  * API for filtering a set of refs. Based on the type of refs the user | 
 |  * has requested, we iterate through those refs and apply filters | 
 |  * as per the given ref_filter structure and finally store the | 
 |  * filtered refs in the ref_array structure. | 
 |  */ | 
 | int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type) | 
 | { | 
 | 	struct ref_filter_cbdata ref_cbdata; | 
 | 	int ret = 0; | 
 | 	unsigned int broken = 0; | 
 |  | 
 | 	ref_cbdata.array = array; | 
 | 	ref_cbdata.filter = filter; | 
 |  | 
 | 	if (type & FILTER_REFS_INCLUDE_BROKEN) | 
 | 		broken = 1; | 
 | 	filter->kind = type & FILTER_REFS_KIND_MASK; | 
 |  | 
 | 	/*  Simple per-ref filtering */ | 
 | 	if (!filter->kind) | 
 | 		die("filter_refs: invalid type"); | 
 | 	else { | 
 | 		/* | 
 | 		 * For common cases where we need only branches or remotes or tags, | 
 | 		 * we only iterate through those refs. If a mix of refs is needed, | 
 | 		 * we iterate over all refs and filter out required refs with the help | 
 | 		 * of filter_ref_kind(). | 
 | 		 */ | 
 | 		if (filter->kind == FILTER_REFS_BRANCHES) | 
 | 			ret = for_each_fullref_in("refs/heads/", ref_filter_handler, &ref_cbdata, broken); | 
 | 		else if (filter->kind == FILTER_REFS_REMOTES) | 
 | 			ret = for_each_fullref_in("refs/remotes/", ref_filter_handler, &ref_cbdata, broken); | 
 | 		else if (filter->kind == FILTER_REFS_TAGS) | 
 | 			ret = for_each_fullref_in("refs/tags/", ref_filter_handler, &ref_cbdata, broken); | 
 | 		else if (filter->kind & FILTER_REFS_ALL) | 
 | 			ret = for_each_fullref_in("", ref_filter_handler, &ref_cbdata, broken); | 
 | 		if (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD)) | 
 | 			head_ref(ref_filter_handler, &ref_cbdata); | 
 | 	} | 
 |  | 
 |  | 
 | 	/*  Filters that need revision walking */ | 
 | 	if (filter->merge_commit) | 
 | 		do_merge_filter(&ref_cbdata); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, struct ref_array_item *b) | 
 | { | 
 | 	struct atom_value *va, *vb; | 
 | 	int cmp; | 
 | 	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); | 
 | 	if (s->version) | 
 | 		cmp = versioncmp(va->s, vb->s); | 
 | 	else if (cmp_type == FIELD_STR) | 
 | 		cmp = strcmp(va->s, vb->s); | 
 | 	else { | 
 | 		if (va->ul < vb->ul) | 
 | 			cmp = -1; | 
 | 		else if (va->ul == vb->ul) | 
 | 			cmp = strcmp(a->refname, b->refname); | 
 | 		else | 
 | 			cmp = 1; | 
 | 	} | 
 |  | 
 | 	return (s->reverse) ? -cmp : cmp; | 
 | } | 
 |  | 
 | static struct ref_sorting *ref_sorting; | 
 | static int compare_refs(const void *a_, const void *b_) | 
 | { | 
 | 	struct ref_array_item *a = *((struct ref_array_item **)a_); | 
 | 	struct ref_array_item *b = *((struct ref_array_item **)b_); | 
 | 	struct ref_sorting *s; | 
 |  | 
 | 	for (s = ref_sorting; s; s = s->next) { | 
 | 		int cmp = cmp_ref_sorting(s, a, b); | 
 | 		if (cmp) | 
 | 			return cmp; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array) | 
 | { | 
 | 	ref_sorting = sorting; | 
 | 	QSORT(array->items, array->nr, compare_refs); | 
 | } | 
 |  | 
 | static void append_literal(const char *cp, const char *ep, struct ref_formatting_state *state) | 
 | { | 
 | 	struct strbuf *s = &state->stack->output; | 
 |  | 
 | 	while (*cp && (!ep || cp < ep)) { | 
 | 		if (*cp == '%') { | 
 | 			if (cp[1] == '%') | 
 | 				cp++; | 
 | 			else { | 
 | 				int ch = hex2chr(cp + 1); | 
 | 				if (0 <= ch) { | 
 | 					strbuf_addch(s, ch); | 
 | 					cp += 3; | 
 | 					continue; | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 		strbuf_addch(s, *cp); | 
 | 		cp++; | 
 | 	} | 
 | } | 
 |  | 
 | void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style) | 
 | { | 
 | 	const char *cp, *sp, *ep; | 
 | 	struct strbuf *final_buf; | 
 | 	struct ref_formatting_state state = REF_FORMATTING_STATE_INIT; | 
 |  | 
 | 	state.quote_style = quote_style; | 
 | 	push_stack_element(&state.stack); | 
 |  | 
 | 	for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) { | 
 | 		struct atom_value *atomv; | 
 |  | 
 | 		ep = strchr(sp, ')'); | 
 | 		if (cp < sp) | 
 | 			append_literal(cp, sp, &state); | 
 | 		get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv); | 
 | 		atomv->handler(atomv, &state); | 
 | 	} | 
 | 	if (*cp) { | 
 | 		sp = cp + strlen(cp); | 
 | 		append_literal(cp, sp, &state); | 
 | 	} | 
 | 	if (need_color_reset_at_eol) { | 
 | 		struct atom_value resetv; | 
 | 		char color[COLOR_MAXLEN] = ""; | 
 |  | 
 | 		if (color_parse("reset", color) < 0) | 
 | 			die("BUG: couldn't parse 'reset' as a color"); | 
 | 		resetv.s = color; | 
 | 		append_atom(&resetv, &state); | 
 | 	} | 
 | 	if (state.stack->prev) | 
 | 		die(_("format: %%(end) atom missing")); | 
 | 	final_buf = &state.stack->output; | 
 | 	fwrite(final_buf->buf, 1, final_buf->len, stdout); | 
 | 	pop_stack_element(&state.stack); | 
 | 	putchar('\n'); | 
 | } | 
 |  | 
 | /*  If no sorting option is given, use refname to sort as default */ | 
 | struct ref_sorting *ref_default_sorting(void) | 
 | { | 
 | 	static const char cstr_name[] = "refname"; | 
 |  | 
 | 	struct ref_sorting *sorting = xcalloc(1, sizeof(*sorting)); | 
 |  | 
 | 	sorting->next = NULL; | 
 | 	sorting->atom = parse_ref_filter_atom(cstr_name, cstr_name + strlen(cstr_name)); | 
 | 	return sorting; | 
 | } | 
 |  | 
 | int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset) | 
 | { | 
 | 	struct ref_sorting **sorting_tail = opt->value; | 
 | 	struct ref_sorting *s; | 
 | 	int len; | 
 |  | 
 | 	if (!arg) /* should --no-sort void the list ? */ | 
 | 		return -1; | 
 |  | 
 | 	s = xcalloc(1, sizeof(*s)); | 
 | 	s->next = *sorting_tail; | 
 | 	*sorting_tail = s; | 
 |  | 
 | 	if (*arg == '-') { | 
 | 		s->reverse = 1; | 
 | 		arg++; | 
 | 	} | 
 | 	if (skip_prefix(arg, "version:", &arg) || | 
 | 	    skip_prefix(arg, "v:", &arg)) | 
 | 		s->version = 1; | 
 | 	len = strlen(arg); | 
 | 	s->atom = parse_ref_filter_atom(arg, arg+len); | 
 | 	return 0; | 
 | } | 
 |  | 
 | int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset) | 
 | { | 
 | 	struct ref_filter *rf = opt->value; | 
 | 	unsigned char sha1[20]; | 
 |  | 
 | 	rf->merge = starts_with(opt->long_name, "no") | 
 | 		? REF_FILTER_MERGED_OMIT | 
 | 		: REF_FILTER_MERGED_INCLUDE; | 
 |  | 
 | 	if (get_sha1(arg, sha1)) | 
 | 		die(_("malformed object name %s"), arg); | 
 |  | 
 | 	rf->merge_commit = lookup_commit_reference_gently(sha1, 0); | 
 | 	if (!rf->merge_commit) | 
 | 		return opterror(opt, "must point to a commit", 0); | 
 |  | 
 | 	return 0; | 
 | } |