for-each-ref: avoid color leakage

To make sure that an invocation like the following doesn't leak color, $ git for-each-ref --format='%(subject)%(color:green)' auto-reset at the end of the format string when the last color token seen in the format string isn't a color-reset. Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Ramkumar Ramachandra committed Nov 18, 2013 at 23:09 UTC db64eb655b48ea2c635480c6cc992b0156817aeb
2 files changed +24 -6
builtin/for-each-ref.c
+23 -5
@@ -93,6 +93,7 @@ static struct {
93 static const char **used_atom;
94 static cmp_type *used_atom_type;
95 static int used_atom_cnt, sort_atom_limit, need_tagged, need_symref;
96 +static int need_color_reset_at_eol;
97
98 /*
99 * Used to parse format string and sort specifiers
@@ -179,13 +180,21 @@ static const char *find_next(const char *cp)
180 static int verify_format(const char *format)
181 {
182 const char *cp, *sp;
183 + static const char color_reset[] = "color:reset";
184 +
185 + need_color_reset_at_eol = 0;
186 for (cp = format; *cp && (sp = find_next(cp)); ) {
187 const char *ep = strchr(sp, ')');
188 + int at;
189 +
190 if (!ep)
191 return error("malformed format string %s", sp);
192 /* sp points at "%(" and ep points at the closing ")" */
187 - parse_atom(sp + 2, ep);
193 + at = parse_atom(sp + 2, ep);
194 cp = ep + 1;
195 +
196 + if (!memcmp(used_atom[at], "color:", 6))
197 + need_color_reset_at_eol = !!strcmp(used_atom[at], color_reset);
198 }
199 return 0;
200 }
@@ -914,11 +923,9 @@ static void sort_refs(struct ref_sort *sort, struct refinfo **refs, int num_refs
923 qsort(refs, num_refs, sizeof(struct refinfo *), compare_refs);
924 }
925
917 -static void print_value(struct refinfo *ref, int atom, int quote_style)
926 +static void print_value(struct atom_value *v, int quote_style)
927 {
919 - struct atom_value *v;
928 struct strbuf sb = STRBUF_INIT;
921 - get_value(ref, atom, &v);
929 switch (quote_style) {
930 case QUOTE_NONE:
931 fputs(v->s, stdout);
@@ -985,15 +992,26 @@ static void show_ref(struct refinfo *info, const char *format, int quote_style)
992 const char *cp, *sp, *ep;
993
994 for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {
995 + struct atom_value *atomv;
996 +
997 ep = strchr(sp, ')');
998 if (cp < sp)
999 emit(cp, sp);
991 - print_value(info, parse_atom(sp + 2, ep), quote_style);
1000 + get_value(info, parse_atom(sp + 2, ep), &atomv);
1001 + print_value(atomv, quote_style);
1002 }
1003 if (*cp) {
1004 sp = cp + strlen(cp);
1005 emit(cp, sp);
1006 }
1007 + if (need_color_reset_at_eol) {
1008 + struct atom_value resetv;
1009 + char color[COLOR_MAXLEN] = "";
1010 +
1011 + color_parse("reset", "--format", color);
1012 + resetv.s = color;
1013 + print_value(&resetv, quote_style);
1014 + }
1015 putchar('\n');
1016 }
1017
t/t6300-for-each-ref.sh
+1 -1
@@ -356,7 +356,7 @@ $(git rev-parse --short refs/tags/two) $(get_color green)two$(get_color reset)
356 EOF
357
358 test_expect_success 'Check %(color:...) ' '
359 - git for-each-ref --format="%(objectname:short) %(color:green)%(refname:short)%(color:reset)" >actual &&
359 + git for-each-ref --format="%(objectname:short) %(color:green)%(refname:short)" >actual &&
360 test_cmp expected actual
361 '
362