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