@@ -26,6 +26,12 @@ static struct keyword_entry keywords[] = {
2626 { "error" , GIT_COLOR_BOLD_RED },
2727};
2828
29+ static enum {
30+ ALLOW_NO_CONTROL_CHARACTERS = 0 ,
31+ ALLOW_ALL_CONTROL_CHARACTERS = 1 ,
32+ ALLOW_ANSI_COLOR_SEQUENCES = 2
33+ } allow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES ;
34+
2935/* Returns a color setting (GIT_COLOR_NEVER, etc). */
3036static int use_sideband_colors (void )
3137{
@@ -39,6 +45,25 @@ static int use_sideband_colors(void)
3945 if (use_sideband_colors_cached >= 0 )
4046 return use_sideband_colors_cached ;
4147
48+ switch (git_config_get_maybe_bool ("sideband.allowcontrolcharacters" , & i )) {
49+ case 0 : /* Boolean value */
50+ allow_control_characters = i ? ALLOW_ALL_CONTROL_CHARACTERS :
51+ ALLOW_NO_CONTROL_CHARACTERS ;
52+ break ;
53+ case -1 : /* non-Boolean value */
54+ if (git_config_get_string_tmp ("sideband.allowcontrolcharacters" ,
55+ & value ))
56+ ; /* huh? `get_maybe_bool()` returned -1 */
57+ else if (!strcmp (value , "color" ))
58+ allow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES ;
59+ else
60+ warning (_ ("unrecognized value for `sideband."
61+ "allowControlCharacters`: '%s'" ), value );
62+ break ;
63+ default :
64+ break ; /* not configured */
65+ }
66+
4267 if (!git_config_get_string_tmp (key , & value ))
4368 use_sideband_colors_cached = git_config_colorbool (key , value );
4469 else if (!git_config_get_string_tmp ("color.ui" , & value ))
@@ -66,6 +91,55 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref
6691 list_config_item (list , prefix , keywords [i ].keyword );
6792}
6893
94+ static int handle_ansi_color_sequence (struct strbuf * dest , const char * src , int n )
95+ {
96+ int i ;
97+
98+ /*
99+ * Valid ANSI color sequences are of the form
100+ *
101+ * ESC [ [<n> [; <n>]*] m
102+ */
103+
104+ if (allow_control_characters != ALLOW_ANSI_COLOR_SEQUENCES ||
105+ n < 3 || src [0 ] != '\x1b' || src [1 ] != '[' )
106+ return 0 ;
107+
108+ for (i = 2 ; i < n ; i ++ ) {
109+ if (src [i ] == 'm' ) {
110+ strbuf_add (dest , src , i + 1 );
111+ return i ;
112+ }
113+ if (!isdigit (src [i ]) && src [i ] != ';' )
114+ break ;
115+ }
116+
117+ return 0 ;
118+ }
119+
120+ static void strbuf_add_sanitized (struct strbuf * dest , const char * src , int n )
121+ {
122+ int i ;
123+
124+ if (allow_control_characters == ALLOW_ALL_CONTROL_CHARACTERS ) {
125+ strbuf_add (dest , src , n );
126+ return ;
127+ }
128+
129+ strbuf_grow (dest , n );
130+ for (; n && * src ; src ++ , n -- ) {
131+ if (!iscntrl (* src ) || * src == '\t' || * src == '\n' )
132+ strbuf_addch (dest , * src );
133+ else if ((i = handle_ansi_color_sequence (dest , src , n ))) {
134+ src += i ;
135+ n -= i ;
136+ } else {
137+ strbuf_addch (dest , '^' );
138+ strbuf_addch (dest , 0x40 + * src );
139+ }
140+ }
141+ }
142+
69143/*
70144 * Optionally highlight one keyword in remote output if it appears at the start
71145 * of the line. This should be called for a single line only, which is
@@ -81,7 +155,7 @@ static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)
81155 int i ;
82156
83157 if (!want_color_stderr (use_sideband_colors ())) {
84- strbuf_add (dest , src , n );
158+ strbuf_add_sanitized (dest , src , n );
85159 return ;
86160 }
87161
@@ -114,7 +188,7 @@ static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)
114188 }
115189 }
116190
117- strbuf_add (dest , src , n );
191+ strbuf_add_sanitized (dest , src , n );
118192}
119193
120194
0 commit comments