@@ -29,9 +29,43 @@ static struct keyword_entry keywords[] = {
2929static enum {
3030 ALLOW_NO_CONTROL_CHARACTERS = 0 ,
3131 ALLOW_ANSI_COLOR_SEQUENCES = 1 <<0 ,
32+ ALLOW_ANSI_CURSOR_MOVEMENTS = 1 <<1 ,
33+ ALLOW_ANSI_ERASE = 1 <<2 ,
3234 ALLOW_DEFAULT_ANSI_SEQUENCES = ALLOW_ANSI_COLOR_SEQUENCES ,
33- ALLOW_ALL_CONTROL_CHARACTERS = 1 <<1 ,
34- } allow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES ;
35+ ALLOW_ALL_CONTROL_CHARACTERS = 1 <<3 ,
36+ } allow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES ;
37+
38+ static inline int skip_prefix_in_csv (const char * value , const char * prefix ,
39+ const char * * out )
40+ {
41+ if (!skip_prefix (value , prefix , & value ) ||
42+ (* value && * value != ',' ))
43+ return 0 ;
44+ * out = value + !!* value ;
45+ return 1 ;
46+ }
47+
48+ static void parse_allow_control_characters (const char * value )
49+ {
50+ allow_control_characters = ALLOW_NO_CONTROL_CHARACTERS ;
51+ while (* value ) {
52+ if (skip_prefix_in_csv (value , "default" , & value ))
53+ allow_control_characters |= ALLOW_DEFAULT_ANSI_SEQUENCES ;
54+ else if (skip_prefix_in_csv (value , "color" , & value ))
55+ allow_control_characters |= ALLOW_ANSI_COLOR_SEQUENCES ;
56+ else if (skip_prefix_in_csv (value , "cursor" , & value ))
57+ allow_control_characters |= ALLOW_ANSI_CURSOR_MOVEMENTS ;
58+ else if (skip_prefix_in_csv (value , "erase" , & value ))
59+ allow_control_characters |= ALLOW_ANSI_ERASE ;
60+ else if (skip_prefix_in_csv (value , "true" , & value ))
61+ allow_control_characters = ALLOW_ALL_CONTROL_CHARACTERS ;
62+ else if (skip_prefix_in_csv (value , "false" , & value ))
63+ allow_control_characters = ALLOW_NO_CONTROL_CHARACTERS ;
64+ else
65+ warning (_ ("unrecognized value for `sideband."
66+ "allowControlCharacters`: '%s'" ), value );
67+ }
68+ }
3569
3670/* Returns a color setting (GIT_COLOR_NEVER, etc). */
3771static enum git_colorbool use_sideband_colors (void )
@@ -55,13 +89,8 @@ static enum git_colorbool use_sideband_colors(void)
5589 if (repo_config_get_string_tmp (the_repository , "sideband.allowcontrolcharacters" ,
5690 & value ))
5791 ; /* huh? `get_maybe_bool()` returned -1 */
58- else if (!strcmp (value , "default" ))
59- allow_control_characters = ALLOW_DEFAULT_ANSI_SEQUENCES ;
60- else if (!strcmp (value , "color" ))
61- allow_control_characters = ALLOW_ANSI_COLOR_SEQUENCES ;
6292 else
63- warning (_ ("unrecognized value for `sideband."
64- "allowControlCharacters`: '%s'" ), value );
93+ parse_allow_control_characters (value );
6594 break ;
6695 default :
6796 break ; /* not configured */
@@ -94,7 +123,7 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref
94123 list_config_item (list , prefix , keywords [i ].keyword );
95124}
96125
97- static int handle_ansi_color_sequence (struct strbuf * dest , const char * src , int n )
126+ static int handle_ansi_sequence (struct strbuf * dest , const char * src , int n )
98127{
99128 int i ;
100129
@@ -106,14 +135,47 @@ static int handle_ansi_color_sequence(struct strbuf *dest, const char *src, int
106135 * These are part of the Select Graphic Rendition sequences which
107136 * contain more than just color sequences, for more details see
108137 * https://en.wikipedia.org/wiki/ANSI_escape_code#SGR.
138+ *
139+ * The cursor movement sequences are:
140+ *
141+ * ESC [ n A - Cursor up n lines (CUU)
142+ * ESC [ n B - Cursor down n lines (CUD)
143+ * ESC [ n C - Cursor forward n columns (CUF)
144+ * ESC [ n D - Cursor back n columns (CUB)
145+ * ESC [ n E - Cursor next line, beginning (CNL)
146+ * ESC [ n F - Cursor previous line, beginning (CPL)
147+ * ESC [ n G - Cursor to column n (CHA)
148+ * ESC [ n ; m H - Cursor position (row n, col m) (CUP)
149+ * ESC [ n ; m f - Same as H (HVP)
150+ *
151+ * The sequences to erase characters are:
152+ *
153+ *
154+ * ESC [ 0 J - Clear from cursor to end of screen (ED)
155+ * ESC [ 1 J - Clear from cursor to beginning of screen (ED)
156+ * ESC [ 2 J - Clear entire screen (ED)
157+ * ESC [ 3 J - Clear entire screen + scrollback (ED) - xterm extension
158+ * ESC [ 0 K - Clear from cursor to end of line (EL)
159+ * ESC [ 1 K - Clear from cursor to beginning of line (EL)
160+ * ESC [ 2 K - Clear entire line (EL)
161+ * ESC [ n M - Delete n lines (DL)
162+ * ESC [ n P - Delete n characters (DCH)
163+ * ESC [ n X - Erase n characters (ECH)
164+ *
165+ * For a comprehensive list of common ANSI Escape sequences, see
166+ * https://www.xfree86.org/current/ctlseqs.html
109167 */
110168
111- if (allow_control_characters != ALLOW_ANSI_COLOR_SEQUENCES ||
112- n < 3 || src [0 ] != '\x1b' || src [1 ] != '[' )
169+ if (n < 3 || src [0 ] != '\x1b' || src [1 ] != '[' )
113170 return 0 ;
114171
115172 for (i = 2 ; i < n ; i ++ ) {
116- if (src [i ] == 'm' ) {
173+ if (((allow_control_characters & ALLOW_ANSI_COLOR_SEQUENCES ) &&
174+ src [i ] == 'm' ) ||
175+ ((allow_control_characters & ALLOW_ANSI_CURSOR_MOVEMENTS ) &&
176+ strchr ("ABCDEFGHf" , src [i ])) ||
177+ ((allow_control_characters & ALLOW_ANSI_ERASE ) &&
178+ strchr ("JKMPX" , src [i ]))) {
117179 strbuf_add (dest , src , i + 1 );
118180 return i ;
119181 }
@@ -128,7 +190,7 @@ static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)
128190{
129191 int i ;
130192
131- if (allow_control_characters == ALLOW_ALL_CONTROL_CHARACTERS ) {
193+ if (( allow_control_characters & ALLOW_ALL_CONTROL_CHARACTERS ) ) {
132194 strbuf_add (dest , src , n );
133195 return ;
134196 }
@@ -137,7 +199,8 @@ static void strbuf_add_sanitized(struct strbuf *dest, const char *src, int n)
137199 for (; n && * src ; src ++ , n -- ) {
138200 if (!iscntrl (* src ) || * src == '\t' || * src == '\n' ) {
139201 strbuf_addch (dest , * src );
140- } else if ((i = handle_ansi_color_sequence (dest , src , n ))) {
202+ } else if (allow_control_characters != ALLOW_NO_CONTROL_CHARACTERS &&
203+ (i = handle_ansi_sequence (dest , src , n ))) {
141204 src += i ;
142205 n -= i ;
143206 } else {
0 commit comments