ref-filter.c 65.1 KB
Newer Older
1 2 3 4 5
#include "builtin.h"
#include "cache.h"
#include "parse-options.h"
#include "refs.h"
#include "wildmatch.h"
6
#include "object-store.h"
7
#include "repository.h"
8 9 10 11 12 13
#include "commit.h"
#include "remote.h"
#include "color.h"
#include "tag.h"
#include "quote.h"
#include "ref-filter.h"
14
#include "revision.h"
15
#include "utf8.h"
16 17
#include "git-compat-util.h"
#include "version.h"
18
#include "trailer.h"
19
#include "wt-status.h"
20
#include "commit-slab.h"
21
#include "commit-graph.h"
22
#include "commit-reach.h"
23

24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
static struct ref_msg {
	const char *gone;
	const char *ahead;
	const char *behind;
	const char *ahead_behind;
} msgs = {
	 /* Untranslated plumbing messages: */
	"gone",
	"ahead %d",
	"behind %d",
	"ahead %d, behind %d"
};

void setup_ref_filter_porcelain_msg(void)
{
	msgs.gone = _("gone");
	msgs.ahead = _("ahead %d");
	msgs.behind = _("behind %d");
	msgs.ahead_behind = _("ahead %d, behind %d");
}
44 45

typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;
46
typedef enum { COMPARE_EQUAL, COMPARE_UNEQUAL, COMPARE_NONE } cmp_status;
47
typedef enum { SOURCE_NONE = 0, SOURCE_OBJ, SOURCE_OTHER } info_source;
48

49 50 51 52 53
struct align {
	align_type position;
	unsigned int width;
};

54
struct if_then_else {
55 56
	cmp_status cmp_status;
	const char *str;
57 58 59 60 61
	unsigned int then_atom_seen : 1,
		else_atom_seen : 1,
		condition_satisfied : 1;
};

62
struct refname_atom {
63 64
	enum { R_NORMAL, R_SHORT, R_LSTRIP, R_RSTRIP } option;
	int lstrip, rstrip;
65 66
};

67 68 69 70 71 72 73 74 75 76 77
static struct expand_data {
	struct object_id oid;
	enum object_type type;
	unsigned long size;
	off_t disk_size;
	struct object_id delta_base_oid;
	void *content;

	struct object_info info;
} oi, oi_deref;

78 79 80 81 82 83 84 85 86 87
/*
 * 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.
 */
88 89 90
static struct used_atom {
	const char *name;
	cmp_type type;
91
	info_source source;
92 93
	union {
		char color[COLOR_MAXLEN];
94
		struct align align;
95
		struct {
96
			enum {
97
				RR_REF, RR_TRACK, RR_TRACKSHORT, RR_REMOTE_NAME, RR_REMOTE_REF
98
			} option;
99
			struct refname_atom refname;
100
			unsigned int nobracket : 1, push : 1, push_remote : 1;
101
		} remote_ref;
102
		struct {
103
			enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB, C_TRAILERS } option;
104
			struct process_trailer_options trailer_opts;
105 106
			unsigned int nlines;
		} contents;
107 108 109 110
		struct {
			cmp_status cmp_status;
			const char *str;
		} if_then_else;
111 112 113 114
		struct {
			enum { O_FULL, O_LENGTH, O_SHORT } option;
			unsigned int length;
		} objectname;
115
		struct refname_atom refname;
116
		char *head;
117
	} u;
118
} *used_atom;
119 120
static int used_atom_cnt, need_tagged, need_symref;

121 122 123 124 125 126 127 128 129 130 131 132 133
/*
 * Expand string, append it to strbuf *sb, then return error code ret.
 * Allow to save few lines of code.
 */
static int strbuf_addf_ret(struct strbuf *sb, int ret, const char *fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);
	strbuf_vaddf(sb, fmt, ap);
	va_end(ap);
	return ret;
}

134 135
static int color_atom_parser(const struct ref_format *format, struct used_atom *atom,
			     const char *color_value, struct strbuf *err)
136 137
{
	if (!color_value)
138
		return strbuf_addf_ret(err, -1, _("expected format: %%(color:<color>)"));
139
	if (color_parse(color_value, atom->u.color) < 0)
140 141
		return strbuf_addf_ret(err, -1, _("unrecognized color: %%(color:%s)"),
				       color_value);
142 143 144 145 146 147
	/*
	 * We check this after we've parsed the color, which lets us complain
	 * about syntactically bogus color names even if they won't be used.
	 */
	if (!want_color(format->use_color))
		color_parse("", atom->u.color);
148
	return 0;
149 150
}

151 152
static int refname_atom_parser_internal(struct refname_atom *atom, const char *arg,
					 const char *name, struct strbuf *err)
153 154
{
	if (!arg)
155
		atom->option = R_NORMAL;
156
	else if (!strcmp(arg, "short"))
157
		atom->option = R_SHORT;
158 159
	else if (skip_prefix(arg, "lstrip=", &arg) ||
		 skip_prefix(arg, "strip=", &arg)) {
160
		atom->option = R_LSTRIP;
161
		if (strtol_i(arg, 10, &atom->lstrip))
162
			return strbuf_addf_ret(err, -1, _("Integer value expected refname:lstrip=%s"), arg);
163 164 165
	} else if (skip_prefix(arg, "rstrip=", &arg)) {
		atom->option = R_RSTRIP;
		if (strtol_i(arg, 10, &atom->rstrip))
166
			return strbuf_addf_ret(err, -1, _("Integer value expected refname:rstrip=%s"), arg);
167
	} else
168 169
		return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), name, arg);
	return 0;
170 171
}

172 173
static int remote_ref_atom_parser(const struct ref_format *format, struct used_atom *atom,
				  const char *arg, struct strbuf *err)
174
{
175 176 177
	struct string_list params = STRING_LIST_INIT_DUP;
	int i;

178 179 180
	if (!strcmp(atom->name, "push") || starts_with(atom->name, "push:"))
		atom->u.remote_ref.push = 1;

181
	if (!arg) {
182
		atom->u.remote_ref.option = RR_REF;
183 184
		return refname_atom_parser_internal(&atom->u.remote_ref.refname,
						    arg, atom->name, err);
185 186 187 188 189 190 191 192
	}

	atom->u.remote_ref.nobracket = 0;
	string_list_split(&params, arg, ',', -1);

	for (i = 0; i < params.nr; i++) {
		const char *s = params.items[i].string;

193
		if (!strcmp(s, "track"))
194 195 196 197 198
			atom->u.remote_ref.option = RR_TRACK;
		else if (!strcmp(s, "trackshort"))
			atom->u.remote_ref.option = RR_TRACKSHORT;
		else if (!strcmp(s, "nobracket"))
			atom->u.remote_ref.nobracket = 1;
199 200 201
		else if (!strcmp(s, "remotename")) {
			atom->u.remote_ref.option = RR_REMOTE_NAME;
			atom->u.remote_ref.push_remote = 1;
202 203 204
		} else if (!strcmp(s, "remoteref")) {
			atom->u.remote_ref.option = RR_REMOTE_REF;
			atom->u.remote_ref.push_remote = 1;
205
		} else {
206
			atom->u.remote_ref.option = RR_REF;
207 208 209 210 211
			if (refname_atom_parser_internal(&atom->u.remote_ref.refname,
							 arg, atom->name, err)) {
				string_list_clear(&params, 0);
				return -1;
			}
212
		}
213 214 215
	}

	string_list_clear(&params, 0);
216
	return 0;
217 218
}

219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
static int objecttype_atom_parser(const struct ref_format *format, struct used_atom *atom,
				  const char *arg, struct strbuf *err)
{
	if (arg)
		return strbuf_addf_ret(err, -1, _("%%(objecttype) does not take arguments"));
	if (*atom->name == '*')
		oi_deref.info.typep = &oi_deref.type;
	else
		oi.info.typep = &oi.type;
	return 0;
}

static int objectsize_atom_parser(const struct ref_format *format, struct used_atom *atom,
				  const char *arg, struct strbuf *err)
{
234 235 236 237 238 239 240 241 242 243 244 245
	if (!arg) {
		if (*atom->name == '*')
			oi_deref.info.sizep = &oi_deref.size;
		else
			oi.info.sizep = &oi.size;
	} else if (!strcmp(arg, "disk")) {
		if (*atom->name == '*')
			oi_deref.info.disk_sizep = &oi_deref.disk_size;
		else
			oi.info.disk_sizep = &oi.disk_size;
	} else
		return strbuf_addf_ret(err, -1, _("unrecognized %%(objectsize) argument: %s"), arg);
246 247 248
	return 0;
}

249 250
static int deltabase_atom_parser(const struct ref_format *format, struct used_atom *atom,
				 const char *arg, struct strbuf *err)
251 252
{
	if (arg)
253
		return strbuf_addf_ret(err, -1, _("%%(deltabase) does not take arguments"));
254
	if (*atom->name == '*')
255
		oi_deref.info.delta_base_sha1 = oi_deref.delta_base_oid.hash;
256
	else
257
		oi.info.delta_base_sha1 = oi.delta_base_oid.hash;
258 259 260
	return 0;
}

261 262
static int body_atom_parser(const struct ref_format *format, struct used_atom *atom,
			    const char *arg, struct strbuf *err)
263 264
{
	if (arg)
265
		return strbuf_addf_ret(err, -1, _("%%(body) does not take arguments"));
266
	atom->u.contents.option = C_BODY_DEP;
267
	return 0;
268 269
}

270 271
static int subject_atom_parser(const struct ref_format *format, struct used_atom *atom,
			       const char *arg, struct strbuf *err)
272 273
{
	if (arg)
274
		return strbuf_addf_ret(err, -1, _("%%(subject) does not take arguments"));
275
	atom->u.contents.option = C_SUB;
276
	return 0;
277 278
}

279 280
static int trailers_atom_parser(const struct ref_format *format, struct used_atom *atom,
				const char *arg, struct strbuf *err)
281
{
282 283 284
	struct string_list params = STRING_LIST_INIT_DUP;
	int i;

285 286
	atom->u.contents.trailer_opts.no_divider = 1;

287 288 289 290 291 292 293 294
	if (arg) {
		string_list_split(&params, arg, ',', -1);
		for (i = 0; i < params.nr; i++) {
			const char *s = params.items[i].string;
			if (!strcmp(s, "unfold"))
				atom->u.contents.trailer_opts.unfold = 1;
			else if (!strcmp(s, "only"))
				atom->u.contents.trailer_opts.only_trailers = 1;
295 296 297 298 299
			else {
				strbuf_addf(err, _("unknown %%(trailers) argument: %s"), s);
				string_list_clear(&params, 0);
				return -1;
			}
300 301
		}
	}
302
	atom->u.contents.option = C_TRAILERS;
303
	string_list_clear(&params, 0);
304
	return 0;
305 306
}

307 308
static int contents_atom_parser(const struct ref_format *format, struct used_atom *atom,
				const char *arg, struct strbuf *err)
309 310 311 312 313 314 315 316 317
{
	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;
318 319
	else if (skip_prefix(arg, "trailers", &arg)) {
		skip_prefix(arg, ":", &arg);
320 321
		if (trailers_atom_parser(format, atom, *arg ? arg : NULL, err))
			return -1;
322
	} else if (skip_prefix(arg, "lines=", &arg)) {
323 324
		atom->u.contents.option = C_LINES;
		if (strtoul_ui(arg, 10, &atom->u.contents.nlines))
325
			return strbuf_addf_ret(err, -1, _("positive value expected contents:lines=%s"), arg);
326
	} else
327 328
		return strbuf_addf_ret(err, -1, _("unrecognized %%(contents) argument: %s"), arg);
	return 0;
329 330
}

331 332
static int objectname_atom_parser(const struct ref_format *format, struct used_atom *atom,
				  const char *arg, struct strbuf *err)
333 334
{
	if (!arg)
335
		atom->u.objectname.option = O_FULL;
336
	else if (!strcmp(arg, "short"))
337 338 339 340 341
		atom->u.objectname.option = O_SHORT;
	else if (skip_prefix(arg, "short=", &arg)) {
		atom->u.objectname.option = O_LENGTH;
		if (strtoul_ui(arg, 10, &atom->u.objectname.length) ||
		    atom->u.objectname.length == 0)
342
			return strbuf_addf_ret(err, -1, _("positive value expected objectname:short=%s"), arg);
343 344 345
		if (atom->u.objectname.length < MINIMUM_ABBREV)
			atom->u.objectname.length = MINIMUM_ABBREV;
	} else
346 347
		return strbuf_addf_ret(err, -1, _("unrecognized %%(objectname) argument: %s"), arg);
	return 0;
348 349
}

350 351
static int refname_atom_parser(const struct ref_format *format, struct used_atom *atom,
			       const char *arg, struct strbuf *err)
352
{
353
	return refname_atom_parser_internal(&atom->u.refname, arg, atom->name, err);
354 355
}

356 357 358 359 360 361 362 363 364 365 366
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;
}

367 368
static int align_atom_parser(const struct ref_format *format, struct used_atom *atom,
			     const char *arg, struct strbuf *err)
369 370 371 372 373 374 375
{
	struct align *align = &atom->u.align;
	struct string_list params = STRING_LIST_INIT_DUP;
	int i;
	unsigned int width = ~0U;

	if (!arg)
376
		return strbuf_addf_ret(err, -1, _("expected format: %%(align:<width>,<position>)"));
377 378 379 380 381 382 383 384

	align->position = ALIGN_LEFT;

	string_list_split(&params, arg, ',', -1);
	for (i = 0; i < params.nr; i++) {
		const char *s = params.items[i].string;
		int position;

385 386
		if (skip_prefix(s, "position=", &s)) {
			position = parse_align_position(s);
387 388 389 390 391
			if (position < 0) {
				strbuf_addf(err, _("unrecognized position:%s"), s);
				string_list_clear(&params, 0);
				return -1;
			}
392 393
			align->position = position;
		} else if (skip_prefix(s, "width=", &s)) {
394 395 396 397 398
			if (strtoul_ui(s, 10, &width)) {
				strbuf_addf(err, _("unrecognized width:%s"), s);
				string_list_clear(&params, 0);
				return -1;
			}
399
		} else if (!strtoul_ui(s, 10, &width))
400 401 402
			;
		else if ((position = parse_align_position(s)) >= 0)
			align->position = position;
403 404 405 406 407
		else {
			strbuf_addf(err, _("unrecognized %%(align) argument: %s"), s);
			string_list_clear(&params, 0);
			return -1;
		}
408 409
	}

410 411 412 413
	if (width == ~0U) {
		string_list_clear(&params, 0);
		return strbuf_addf_ret(err, -1, _("positive width expected with the %%(align) atom"));
	}
414 415
	align->width = width;
	string_list_clear(&params, 0);
416
	return 0;
417 418
}

419 420
static int if_atom_parser(const struct ref_format *format, struct used_atom *atom,
			  const char *arg, struct strbuf *err)
421 422 423
{
	if (!arg) {
		atom->u.if_then_else.cmp_status = COMPARE_NONE;
424
		return 0;
425 426 427 428
	} else if (skip_prefix(arg, "equals=", &atom->u.if_then_else.str)) {
		atom->u.if_then_else.cmp_status = COMPARE_EQUAL;
	} else if (skip_prefix(arg, "notequals=", &atom->u.if_then_else.str)) {
		atom->u.if_then_else.cmp_status = COMPARE_UNEQUAL;
429 430 431
	} else
		return strbuf_addf_ret(err, -1, _("unrecognized %%(if) argument: %s"), arg);
	return 0;
432 433
}

434 435
static int head_atom_parser(const struct ref_format *format, struct used_atom *atom,
			    const char *arg, struct strbuf *unused_err)
436
{
437
	atom->u.head = resolve_refdup("HEAD", RESOLVE_REF_READING, NULL, NULL);
438
	return 0;
439
}
440

441 442
static struct {
	const char *name;
443
	info_source source;
444
	cmp_type cmp_type;
445 446
	int (*parser)(const struct ref_format *format, struct used_atom *atom,
		      const char *arg, struct strbuf *err);
447
} valid_atom[] = {
448
	{ "refname", SOURCE_NONE, FIELD_STR, refname_atom_parser },
449 450
	{ "objecttype", SOURCE_OTHER, FIELD_STR, objecttype_atom_parser },
	{ "objectsize", SOURCE_OTHER, FIELD_ULONG, objectsize_atom_parser },
451
	{ "objectname", SOURCE_OTHER, FIELD_STR, objectname_atom_parser },
452
	{ "deltabase", SOURCE_OTHER, FIELD_STR, deltabase_atom_parser },
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
	{ "tree", SOURCE_OBJ },
	{ "parent", SOURCE_OBJ },
	{ "numparent", SOURCE_OBJ, FIELD_ULONG },
	{ "object", SOURCE_OBJ },
	{ "type", SOURCE_OBJ },
	{ "tag", SOURCE_OBJ },
	{ "author", SOURCE_OBJ },
	{ "authorname", SOURCE_OBJ },
	{ "authoremail", SOURCE_OBJ },
	{ "authordate", SOURCE_OBJ, FIELD_TIME },
	{ "committer", SOURCE_OBJ },
	{ "committername", SOURCE_OBJ },
	{ "committeremail", SOURCE_OBJ },
	{ "committerdate", SOURCE_OBJ, FIELD_TIME },
	{ "tagger", SOURCE_OBJ },
	{ "taggername", SOURCE_OBJ },
	{ "taggeremail", SOURCE_OBJ },
	{ "taggerdate", SOURCE_OBJ, FIELD_TIME },
	{ "creator", SOURCE_OBJ },
	{ "creatordate", SOURCE_OBJ, FIELD_TIME },
	{ "subject", SOURCE_OBJ, FIELD_STR, subject_atom_parser },
	{ "body", SOURCE_OBJ, FIELD_STR, body_atom_parser },
	{ "trailers", SOURCE_OBJ, FIELD_STR, trailers_atom_parser },
	{ "contents", SOURCE_OBJ, FIELD_STR, contents_atom_parser },
	{ "upstream", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser },
	{ "push", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser },
	{ "symref", SOURCE_NONE, FIELD_STR, refname_atom_parser },
	{ "flag", SOURCE_NONE },
	{ "HEAD", SOURCE_NONE, FIELD_STR, head_atom_parser },
	{ "color", SOURCE_NONE, FIELD_STR, color_atom_parser },
	{ "align", SOURCE_NONE, FIELD_STR, align_atom_parser },
	{ "end", SOURCE_NONE },
	{ "if", SOURCE_NONE, FIELD_STR, if_atom_parser },
	{ "then", SOURCE_NONE },
	{ "else", SOURCE_NONE },
488 489 490 491
	/*
	 * Please update $__git_ref_fieldlist in git-completion.bash
	 * when you add new atoms
	 */
492 493
};

494 495 496 497 498
#define REF_FORMATTING_STATE_INIT  { 0, NULL }

struct ref_formatting_stack {
	struct ref_formatting_stack *prev;
	struct strbuf output;
499
	void (*at_end)(struct ref_formatting_stack **stack);
500
	void *at_end_data;
501 502 503 504 505 506 507
};

struct ref_formatting_state {
	int quote_style;
	struct ref_formatting_stack *stack;
};

508 509
struct atom_value {
	const char *s;
510 511
	int (*handler)(struct atom_value *atomv, struct ref_formatting_state *state,
		       struct strbuf *err);
512
	uintmax_t value; /* used for sorting when not FIELD_STR */
513
	struct used_atom *atom;
514 515
};

516 517 518
/*
 * Used to parse format string and sort specifiers
 */
519
static int parse_ref_filter_atom(const struct ref_format *format,
520 521
				 const char *atom, const char *ep,
				 struct strbuf *err)
522 523
{
	const char *sp;
524
	const char *arg;
525
	int i, at, atom_len;
526 527 528 529 530

	sp = atom;
	if (*sp == '*' && sp < ep)
		sp++; /* deref */
	if (ep <= sp)
531 532
		return strbuf_addf_ret(err, -1, _("malformed field name: %.*s"),
				       (int)(ep-atom), atom);
533 534 535

	/* Do we have the atom already used elsewhere? */
	for (i = 0; i < used_atom_cnt; i++) {
536 537
		int len = strlen(used_atom[i].name);
		if (len == ep - atom && !memcmp(used_atom[i].name, atom, len))
538 539 540
			return i;
	}

541 542 543 544 545 546 547 548 549
	/*
	 * 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;

550 551 552
	/* Is the atom a valid one? */
	for (i = 0; i < ARRAY_SIZE(valid_atom); i++) {
		int len = strlen(valid_atom[i].name);
553
		if (len == atom_len && !memcmp(valid_atom[i].name, sp, len))
554 555 556 557
			break;
	}

	if (ARRAY_SIZE(valid_atom) <= i)
558 559
		return strbuf_addf_ret(err, -1, _("unknown field name: %.*s"),
				       (int)(ep-atom), atom);
560 561 562 563
	if (valid_atom[i].source != SOURCE_NONE && !have_git_dir())
		return strbuf_addf_ret(err, -1,
				       _("not a git repository, but the field '%.*s' requires access to object data"),
				       (int)(ep-atom), atom);
564 565 566 567 568

	/* Add it in, including the deref prefix */
	at = used_atom_cnt;
	used_atom_cnt++;
	REALLOC_ARRAY(used_atom, used_atom_cnt);
569 570
	used_atom[at].name = xmemdupz(atom, ep - atom);
	used_atom[at].type = valid_atom[i].cmp_type;
571
	used_atom[at].source = valid_atom[i].source;
572 573 574 575 576 577
	if (used_atom[at].source == SOURCE_OBJ) {
		if (*atom == '*')
			oi_deref.info.contentp = &oi_deref.content;
		else
			oi.info.contentp = &oi.content;
	}
578
	if (arg) {
579
		arg = used_atom[at].name + (arg - atom) + 1;
580 581 582 583 584 585 586 587
		if (!*arg) {
			/*
			 * Treat empty sub-arguments list as NULL (i.e.,
			 * "%(atom:)" is equivalent to "%(atom)").
			 */
			arg = NULL;
		}
	}
588
	memset(&used_atom[at].u, 0, sizeof(used_atom[at].u));
589 590
	if (valid_atom[i].parser && valid_atom[i].parser(format, &used_atom[at], arg, err))
		return -1;
591 592
	if (*atom == '*')
		need_tagged = 1;
593
	if (!strcmp(valid_atom[i].name, "symref"))
594 595 596 597
		need_symref = 1;
	return at;
}

598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618
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;
	}
}

619 620
static int append_atom(struct atom_value *v, struct ref_formatting_state *state,
		       struct strbuf *unused_err)
621
{
622 623 624 625 626 627 628 629 630 631
	/*
	 * 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);
632
	return 0;
633 634
}

635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
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, &current->output);
	strbuf_release(&current->output);
	free(current);
	*stack = prev;
}

656
static void end_align_handler(struct ref_formatting_stack **stack)
657
{
658 659
	struct ref_formatting_stack *cur = *stack;
	struct align *align = (struct align *)cur->at_end_data;
660 661
	struct strbuf s = STRBUF_INIT;

662 663
	strbuf_utf8_align(&s, align->position, align->width, cur->output.buf);
	strbuf_swap(&cur->output, &s);
664 665 666
	strbuf_release(&s);
}

667 668
static int align_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
			      struct strbuf *unused_err)
669
{
670
	struct ref_formatting_stack *new_stack;
671 672

	push_stack_element(&state->stack);
673 674 675
	new_stack = state->stack;
	new_stack->at_end = end_align_handler;
	new_stack->at_end_data = &atomv->atom->u.align;
676
	return 0;
677 678
}

679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
static void if_then_else_handler(struct ref_formatting_stack **stack)
{
	struct ref_formatting_stack *cur = *stack;
	struct ref_formatting_stack *prev = cur->prev;
	struct if_then_else *if_then_else = (struct if_then_else *)cur->at_end_data;

	if (!if_then_else->then_atom_seen)
		die(_("format: %%(if) atom used without a %%(then) atom"));

	if (if_then_else->else_atom_seen) {
		/*
		 * There is an %(else) atom: we need to drop one state from the
		 * stack, either the %(else) branch if the condition is satisfied, or
		 * the %(then) branch if it isn't.
		 */
		if (if_then_else->condition_satisfied) {
			strbuf_reset(&cur->output);
			pop_stack_element(&cur);
		} else {
			strbuf_swap(&cur->output, &prev->output);
			strbuf_reset(&cur->output);
			pop_stack_element(&cur);
		}
	} else if (!if_then_else->condition_satisfied) {
		/*
		 * No %(else) atom: just drop the %(then) branch if the
		 * condition is not satisfied.
		 */
		strbuf_reset(&cur->output);
	}

	*stack = cur;
	free(if_then_else);
}

714 715
static int if_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
			   struct strbuf *unused_err)
716
{
717
	struct ref_formatting_stack *new_stack;
718 719
	struct if_then_else *if_then_else = xcalloc(sizeof(struct if_then_else), 1);

720 721 722
	if_then_else->str = atomv->atom->u.if_then_else.str;
	if_then_else->cmp_status = atomv->atom->u.if_then_else.cmp_status;

723
	push_stack_element(&state->stack);
724 725 726
	new_stack = state->stack;
	new_stack->at_end = if_then_else_handler;
	new_stack->at_end_data = if_then_else;
727
	return 0;
728 729 730 731 732 733 734 735 736 737 738 739
}

static int is_empty(const char *s)
{
	while (*s != '\0') {
		if (!isspace(*s))
			return 0;
		s++;
	}
	return 1;
}

740 741
static int then_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
			     struct strbuf *err)
742 743 744 745 746 747 748
{
	struct ref_formatting_stack *cur = state->stack;
	struct if_then_else *if_then_else = NULL;

	if (cur->at_end == if_then_else_handler)
		if_then_else = (struct if_then_else *)cur->at_end_data;
	if (!if_then_else)
749
		return strbuf_addf_ret(err, -1, _("format: %%(then) atom used without an %%(if) atom"));
750
	if (if_then_else->then_atom_seen)
751
		return strbuf_addf_ret(err, -1, _("format: %%(then) atom used more than once"));
752
	if (if_then_else->else_atom_seen)
753
		return strbuf_addf_ret(err, -1, _("format: %%(then) atom used after %%(else)"));
754 755
	if_then_else->then_atom_seen = 1;
	/*
756 757 758
	 * If the 'equals' or 'notequals' attribute is used then
	 * perform the required comparison. If not, only non-empty
	 * strings satisfy the 'if' condition.
759
	 */
760 761 762 763 764 765 766
	if (if_then_else->cmp_status == COMPARE_EQUAL) {
		if (!strcmp(if_then_else->str, cur->output.buf))
			if_then_else->condition_satisfied = 1;
	} else if (if_then_else->cmp_status == COMPARE_UNEQUAL) {
		if (strcmp(if_then_else->str, cur->output.buf))
			if_then_else->condition_satisfied = 1;
	} else if (cur->output.len && !is_empty(cur->output.buf))
767 768
		if_then_else->condition_satisfied = 1;
	strbuf_reset(&cur->output);
769
	return 0;
770 771
}

772 773
static int else_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
			     struct strbuf *err)
774 775 776 777 778 779 780
{
	struct ref_formatting_stack *prev = state->stack;
	struct if_then_else *if_then_else = NULL;

	if (prev->at_end == if_then_else_handler)
		if_then_else = (struct if_then_else *)prev->at_end_data;
	if (!if_then_else)
781
		return strbuf_addf_ret(err, -1, _("format: %%(else) atom used without an %%(if) atom"));
782
	if (!if_then_else->then_atom_seen)
783
		return strbuf_addf_ret(err, -1, _("format: %%(else) atom used without a %%(then) atom"));
784
	if (if_then_else->else_atom_seen)
785
		return strbuf_addf_ret(err, -1, _("format: %%(else) atom used more than once"));
786 787 788 789
	if_then_else->else_atom_seen = 1;
	push_stack_element(&state->stack);
	state->stack->at_end_data = prev->at_end_data;
	state->stack->at_end = prev->at_end;
790
	return 0;
791 792
}

793 794
static int end_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
			    struct strbuf *err)
795 796 797 798 799
{
	struct ref_formatting_stack *current = state->stack;
	struct strbuf s = STRBUF_INIT;

	if (!current->at_end)
800
		return strbuf_addf_ret(err, -1, _("format: %%(end) atom used without corresponding atom"));
801 802 803 804
	current->at_end(&state->stack);

	/*  Stack may have been popped within at_end(), hence reset the current pointer */
	current = state->stack;
805 806 807 808 809 810

	/*
	 * 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.
	 */
811
	if (!current->prev->prev) {
812 813 814 815 816
		quote_formatting(&s, current->output.buf, state->quote_style);
		strbuf_swap(&current->output, &s);
	}
	strbuf_release(&s);
	pop_stack_element(&state->stack);
817
	return 0;
818 819
}

820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845
/*
 * 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.
 */
846
int verify_ref_format(struct ref_format *format)
847 848 849
{
	const char *cp, *sp;

850
	format->need_color_reset_at_eol = 0;
851
	for (cp = format->format; *cp && (sp = find_next(cp)); ) {
852
		struct strbuf err = STRBUF_INIT;
853 854 855 856
		const char *color, *ep = strchr(sp, ')');
		int at;

		if (!ep)
857
			return error(_("malformed format string %s"), sp);
858
		/* sp points at "%(" and ep points at the closing ")" */
859 860 861
		at = parse_ref_filter_atom(format, sp + 2, ep, &err);
		if (at < 0)
			die("%s", err.buf);
862 863
		cp = ep + 1;

864
		if (skip_prefix(used_atom[at].name, "color:", &color))
865
			format->need_color_reset_at_eol = !!strcmp(color, "reset");
866
		strbuf_release(&err);
867
	}
868 869
	if (format->need_color_reset_at_eol && !want_color(format->use_color))
		format->need_color_reset_at_eol = 0;
870 871 872
	return 0;
}

873
static int grab_objectname(const char *name, const struct object_id *oid,
874
			   struct atom_value *v, struct used_atom *atom)
875
{
876
	if (starts_with(name, "objectname")) {
877
		if (atom->u.objectname.option == O_SHORT) {
878
			v->s = xstrdup(find_unique_abbrev(oid, DEFAULT_ABBREV));
879
			return 1;
880
		} else if (atom->u.objectname.option == O_FULL) {
881
			v->s = xstrdup(oid_to_hex(oid));
882
			return 1;
883
		} else if (atom->u.objectname.option == O_LENGTH) {
884
			v->s = xstrdup(find_unique_abbrev(oid, atom->u.objectname.length));
885
			return 1;
886
		} else
887
			BUG("unknown %%(objectname) option");
888 889 890 891 892
	}
	return 0;
}

/* See grab_values */
893
static void grab_common_values(struct atom_value *val, int deref, struct expand_data *oi)
894 895 896 897
{
	int i;

	for (i = 0; i < used_atom_cnt; i++) {
898
		const char *name = used_atom[i].name;
899 900 901 902 903 904
		struct atom_value *v = &val[i];
		if (!!deref != (*name == '*'))
			continue;
		if (deref)
			name++;
		if (!strcmp(name, "objecttype"))
905
			v->s = xstrdup(type_name(oi->type));
906 907
		else if (!strcmp(name, "objectsize:disk")) {
			v->value = oi->disk_size;
908
			v->s = xstrfmt("%"PRIuMAX, (uintmax_t)oi->disk_size);
909
		} else if (!strcmp(name, "objectsize")) {
910
			v->value = oi->size;
911
			v->s = xstrfmt("%"PRIuMAX , (uintmax_t)oi->size);
912 913
		} else if (!strcmp(name, "deltabase"))
			v->s = xstrdup(oid_to_hex(&oi->delta_base_oid));
914
		else if (deref)
915
			grab_objectname(name, &oi->oid, v, &used_atom[i]);
916 917 918 919
	}
}

/* See grab_values */
920
static void grab_tag_values(struct atom_value *val, int deref, struct object *obj)
921 922 923 924 925
{
	int i;
	struct tag *tag = (struct tag *) obj;

	for (i = 0; i < used_atom_cnt; i++) {
926
		const char *name = used_atom[i].name;
927 928 929 930 931 932
		struct atom_value *v = &val[i];
		if (!!deref != (*name == '*'))
			continue;
		if (deref)
			name++;
		if (!strcmp(name, "tag"))
933
			v->s = xstrdup(tag->tag);
934
		else if (!strcmp(name, "type") && tag->tagged)
935
			v->s = xstrdup(type_name(tag->tagged->type));
936
		else if (!strcmp(name, "object") && tag->tagged)
937
			v->s = xstrdup(oid_to_hex(&tag->tagged->oid));
938 939 940 941
	}
}

/* See grab_values */
942
static void grab_commit_values(struct atom_value *val, int deref, struct object *obj)
943 944 945 946 947
{
	int i;
	struct commit *commit = (struct commit *) obj;

	for (i = 0; i < used_atom_cnt; i++) {
948
		const char *name = used_atom[i].name;
949 950 951 952 953 954
		struct atom_value *v = &val[i];
		if (!!deref != (*name == '*'))
			continue;
		if (deref)
			name++;
		if (!strcmp(name, "tree")) {
955
			v->s = xstrdup(oid_to_hex(get_commit_tree_oid(commit)));
956
		}
957
		else if (!strcmp(name, "numparent")) {
958 959
			v->value = commit_list_count(commit->parents);
			v->s = xstrfmt("%lu", (unsigned long)v->value);
960 961 962
		}
		else if (!strcmp(name, "parent")) {
			struct commit_list *parents;
963 964
			struct strbuf s = STRBUF_INIT;
			for (parents = commit->parents; parents; parents = parents->next) {
965
				struct commit *parent = parents->item;
966 967
				if (parents != commit->parents)
					strbuf_addch(&s, ' ');
968
				strbuf_addstr(&s, oid_to_hex(&parent->object.oid));
969
			}
970
			v->s = strbuf_detach(&s, NULL);
971 972 973 974
		}
	}
}

975
static const char *find_wholine(const char *who, int wholen, const char *buf)
976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036
{
	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;
1037
	timestamp_t timestamp;
1038
	long tz;
1039
	struct date_mode date_mode = { DATE_NORMAL };
1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050
	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++;
1051
		parse_date_format(formatp, &date_mode);
1052 1053 1054 1055
	}

	if (!eoemail)
		goto bad;
1056
	timestamp = parse_timestamp(eoemail + 2, &zone, 10);
1057
	if (timestamp == TIME_MAX)
1058 1059 1060 1061
		goto bad;
	tz = strtol(zone, NULL, 10);
	if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
		goto bad;
1062
	v->s = xstrdup(show_date(timestamp, tz, &date_mode));
1063
	v->value = timestamp;
1064 1065
	return;
 bad:
1066
	v->s = xstrdup("");
1067
	v->value = 0;
1068 1069 1070
}

/* See grab_values */
1071
static void grab_person(const char *who, struct atom_value *val, int deref, void *buf)
1072 1073 1074 1075 1076 1077
{
	int i;
	int wholen = strlen(who);
	const char *wholine = NULL;

	for (i = 0; i < used_atom_cnt; i++) {
1078
		const char *name = used_atom[i].name;
1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091
		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)
1092
			wholine = find_wholine(who, wholen, buf);
1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111
		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)
1112
		wholine = find_wholine(who, wholen, buf);
1113 1114 1115
	if (!wholine)
		return;
	for (i = 0; i < used_atom_cnt; i++) {
1116
		const char *name = used_atom[i].name;
1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129
		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);
	}
}

1130
static void find_subpos(const char *buf,
1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173
			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;
}

1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197
/*
 * 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;
	}
}

1198
/* See grab_values */
1199
static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)
1200 1201 1202 1203 1204 1205
{
	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++) {
1206 1207
		struct used_atom *atom = &used_atom[i];
		const char *name = atom->name;
1208 1209 1210 1211 1212 1213 1214
		struct atom_value *v = &val[i];
		if (!!deref != (*name == '*'))
			continue;
		if (deref)
			name++;
		if (strcmp(name, "subject") &&
		    strcmp(name, "body") &&
1215
		    !starts_with(name, "trailers") &&
1216
		    !starts_with(name, "contents"))
1217 1218
			continue;
		if (!subpos)
1219
			find_subpos(buf,
1220 1221 1222 1223
				    &subpos, &sublen,
				    &bodypos, &bodylen, &nonsiglen,
				    &sigpos, &siglen);

1224
		if (atom->u.contents.option == C_SUB)
1225
			v->s = copy_subject(subpos, sublen);
1226
		else if (atom->u.contents.option == C_BODY_DEP)
1227
			v->s = xmemdupz(bodypos, bodylen);
1228
		else if (atom->u.contents.option == C_BODY)
1229
			v->s = xmemdupz(bodypos, nonsiglen);
1230
		else if (atom->u.contents.option == C_SIG)
1231
			v->s = xmemdupz(sigpos, siglen);
1232
		else if (atom->u.contents.option == C_LINES) {
1233 1234 1235 1236
			struct strbuf s = STRBUF_INIT;
			const char *contents_end = bodylen + bodypos - siglen;

			/*  Size is the length of the message after removing the signature */
1237
			append_lines(&s, subpos, contents_end - subpos, atom->u.contents.nlines);
1238
			v->s = strbuf_detach(&s, NULL);
1239
		} else if (atom->u.contents.option == C_TRAILERS) {
1240
			struct strbuf s = STRBUF_INIT;
1241

1242 1243 1244 1245
			/* Format the trailer info according to the trailer_opts given */
			format_trailers_from_commit(&s, subpos, &atom->u.contents.trailer_opts);

			v->s = strbuf_detach(&s, NULL);
1246 1247
		} else if (atom->u.contents.option == C_BARE)
			v->s = xstrdup(subpos);
1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260
	}
}

/*
 * 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)
1261
			v->s = xstrdup("");
1262 1263 1264 1265 1266 1267 1268 1269 1270 1271
	}
}

/*
 * 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.
 */
1272
static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf)
1273 1274 1275
{
	switch (obj->type) {
	case OBJ_TAG:
1276
		grab_tag_values(val, deref, obj);
1277 1278
		grab_sub_body_contents(val, deref, buf);
		grab_person("tagger", val, deref, buf);
1279 1280
		break;
	case OBJ_COMMIT:
1281
		grab_commit_values(val, deref, obj);
1282 1283 1284
		grab_sub_body_contents(val, deref, buf);
		grab_person("author", val, deref, buf);
		grab_person("committer", val, deref, buf);
1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303
		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;
}

1304
static const char *lstrip_ref_components(const char *refname, int len)
1305
{
1306
	long remaining = len;
1307 1308
	const char *start = xstrdup(refname);
	const char *to_free = start;
1309

1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324
	if (len < 0) {
		int i;
		const char *p = refname;

		/* Find total no of '/' separated path-components */
		for (i = 0; p[i]; p[i] == '/' ? i++ : *p++)
			;
		/*
		 * The number of components we need to strip is now
		 * the total minus the components to be left (Plus one
		 * because we count the number of '/', but the number
		 * of components is one more than the no of '/').
		 */
		remaining = i + len + 1;
	}
1325

1326
	while (remaining > 0) {
1327 1328
		switch (*start++) {
		case '\0':
1329 1330
			free((char *)to_free);
			return xstrdup("");
1331 1332 1333 1334 1335
		case '/':
			remaining--;
			break;
		}
	}
1336

1337 1338
	start = xstrdup(start);
	free((char *)to_free);
1339 1340 1341
	return start;
}

1342 1343 1344
static const char *rstrip_ref_components(const char *refname, int len)
{
	long remaining = len;
1345 1346
	const char *start = xstrdup(refname);
	const char *to_free = start;
1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365

	if (len < 0) {
		int i;
		const char *p = refname;

		/* Find total no of '/' separated path-components */
		for (i = 0; p[i]; p[i] == '/' ? i++ : *p++)
			;
		/*
		 * The number of components we need to strip is now
		 * the total minus the components to be left (Plus one
		 * because we count the number of '/', but the number
		 * of components is one more than the no of '/').
		 */
		remaining = i + len + 1;
	}

	while (remaining-- > 0) {
		char *p = strrchr(start, '/');
1366 1367 1368 1369
		if (p == NULL) {
			free((char *)to_free);
			return xstrdup("");
		} else
1370 1371 1372 1373 1374
			p[0] = '\0';
	}
	return start;
}

1375 1376 1377 1378
static const char *show_ref(struct refname_atom *atom, const char *refname)
{
	if (atom->option == R_SHORT)
		return shorten_unambiguous_ref(refname, warn_ambiguous_refs);
1379 1380
	else if (atom->option == R_LSTRIP)
		return lstrip_ref_components(refname, atom->lstrip);