Skip to content

Commit c09df8a

Browse files
peterhgitster
authored andcommitted
Add ANSI control code emulation for the Windows console
This adds only the minimum necessary to keep git pull/merge's diffstat from wrapping. Notably absent is support for the K (erase) operation, and support for POSIX write. Signed-off-by: Peter Harris <git@peter.is-a-geek.org> Signed-off-by: Johannes Sixt <johannes.sixt@telecom.at> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent b8c5db3 commit c09df8a

File tree

3 files changed

+357
-1
lines changed

3 files changed

+357
-1
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -741,7 +741,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
741741
COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat
742742
COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1
743743
COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
744-
COMPAT_OBJS += compat/mingw.o compat/fnmatch.o compat/regex.o
744+
COMPAT_OBJS += compat/mingw.o compat/fnmatch.o compat/regex.o compat/winansi.o
745745
EXTLIBS += -lws2_32
746746
X = .exe
747747
template_dir = ../share/git-core/templates/

compat/mingw.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,17 @@ static inline unsigned int git_ntohl(unsigned int x)
193193
sig_handler_t mingw_signal(int sig, sig_handler_t handler);
194194
#define signal mingw_signal
195195

196+
/*
197+
* ANSI emulation wrappers
198+
*/
199+
200+
int winansi_fputs(const char *str, FILE *stream);
201+
int winansi_printf(const char *format, ...) __attribute__((format (printf, 1, 2)));
202+
int winansi_fprintf(FILE *stream, const char *format, ...) __attribute__((format (printf, 2, 3)));
203+
#define fputs winansi_fputs
204+
#define printf(...) winansi_printf(__VA_ARGS__)
205+
#define fprintf(...) winansi_fprintf(__VA_ARGS__)
206+
196207
/*
197208
* git specific compatibility
198209
*/

compat/winansi.c

Lines changed: 345 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
1+
/*
2+
* Copyright 2008 Peter Harris <git@peter.is-a-geek.org>
3+
*/
4+
5+
#include <windows.h>
6+
#include "../git-compat-util.h"
7+
8+
/*
9+
Functions to be wrapped:
10+
*/
11+
#undef printf
12+
#undef fprintf
13+
#undef fputs
14+
/* TODO: write */
15+
16+
/*
17+
ANSI codes used by git: m, K
18+
19+
This file is git-specific. Therefore, this file does not attempt
20+
to implement any codes that are not used by git.
21+
22+
TODO: K
23+
*/
24+
25+
static HANDLE console;
26+
static WORD plain_attr;
27+
static WORD attr;
28+
static int negative;
29+
30+
static void init(void)
31+
{
32+
CONSOLE_SCREEN_BUFFER_INFO sbi;
33+
34+
static int initialized = 0;
35+
if (initialized)
36+
return;
37+
38+
console = GetStdHandle(STD_OUTPUT_HANDLE);
39+
if (console == INVALID_HANDLE_VALUE)
40+
console = NULL;
41+
42+
if (!console)
43+
return;
44+
45+
GetConsoleScreenBufferInfo(console, &sbi);
46+
attr = plain_attr = sbi.wAttributes;
47+
negative = 0;
48+
49+
initialized = 1;
50+
}
51+
52+
53+
#define FOREGROUND_ALL (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
54+
#define BACKGROUND_ALL (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)
55+
56+
static void set_console_attr(void)
57+
{
58+
WORD attributes = attr;
59+
if (negative) {
60+
attributes &= ~FOREGROUND_ALL;
61+
attributes &= ~BACKGROUND_ALL;
62+
63+
/* This could probably use a bitmask
64+
instead of a series of ifs */
65+
if (attr & FOREGROUND_RED)
66+
attributes |= BACKGROUND_RED;
67+
if (attr & FOREGROUND_GREEN)
68+
attributes |= BACKGROUND_GREEN;
69+
if (attr & FOREGROUND_BLUE)
70+
attributes |= BACKGROUND_BLUE;
71+
72+
if (attr & BACKGROUND_RED)
73+
attributes |= FOREGROUND_RED;
74+
if (attr & BACKGROUND_GREEN)
75+
attributes |= FOREGROUND_GREEN;
76+
if (attr & BACKGROUND_BLUE)
77+
attributes |= FOREGROUND_BLUE;
78+
}
79+
SetConsoleTextAttribute(console, attributes);
80+
}
81+
82+
static const char *set_attr(const char *str)
83+
{
84+
const char *func;
85+
size_t len = strspn(str, "0123456789;");
86+
func = str + len;
87+
88+
switch (*func) {
89+
case 'm':
90+
do {
91+
long val = strtol(str, (char **)&str, 10);
92+
switch (val) {
93+
case 0: /* reset */
94+
attr = plain_attr;
95+
negative = 0;
96+
break;
97+
case 1: /* bold */
98+
attr |= FOREGROUND_INTENSITY;
99+
break;
100+
case 2: /* faint */
101+
case 22: /* normal */
102+
attr &= ~FOREGROUND_INTENSITY;
103+
break;
104+
case 3: /* italic */
105+
/* Unsupported */
106+
break;
107+
case 4: /* underline */
108+
case 21: /* double underline */
109+
/* Wikipedia says this flag does nothing */
110+
/* Furthermore, mingw doesn't define this flag
111+
attr |= COMMON_LVB_UNDERSCORE; */
112+
break;
113+
case 24: /* no underline */
114+
/* attr &= ~COMMON_LVB_UNDERSCORE; */
115+
break;
116+
case 5: /* slow blink */
117+
case 6: /* fast blink */
118+
/* We don't have blink, but we do have
119+
background intensity */
120+
attr |= BACKGROUND_INTENSITY;
121+
break;
122+
case 25: /* no blink */
123+
attr &= ~BACKGROUND_INTENSITY;
124+
break;
125+
case 7: /* negative */
126+
negative = 1;
127+
break;
128+
case 27: /* positive */
129+
negative = 0;
130+
break;
131+
case 8: /* conceal */
132+
case 28: /* reveal */
133+
/* Unsupported */
134+
break;
135+
case 30: /* Black */
136+
attr &= ~FOREGROUND_ALL;
137+
break;
138+
case 31: /* Red */
139+
attr &= ~FOREGROUND_ALL;
140+
attr |= FOREGROUND_RED;
141+
break;
142+
case 32: /* Green */
143+
attr &= ~FOREGROUND_ALL;
144+
attr |= FOREGROUND_GREEN;
145+
break;
146+
case 33: /* Yellow */
147+
attr &= ~FOREGROUND_ALL;
148+
attr |= FOREGROUND_RED | FOREGROUND_GREEN;
149+
break;
150+
case 34: /* Blue */
151+
attr &= ~FOREGROUND_ALL;
152+
attr |= FOREGROUND_BLUE;
153+
break;
154+
case 35: /* Magenta */
155+
attr &= ~FOREGROUND_ALL;
156+
attr |= FOREGROUND_RED | FOREGROUND_BLUE;
157+
break;
158+
case 36: /* Cyan */
159+
attr &= ~FOREGROUND_ALL;
160+
attr |= FOREGROUND_GREEN | FOREGROUND_BLUE;
161+
break;
162+
case 37: /* White */
163+
attr |= FOREGROUND_RED |
164+
FOREGROUND_GREEN |
165+
FOREGROUND_BLUE;
166+
break;
167+
case 38: /* Unknown */
168+
break;
169+
case 39: /* reset */
170+
attr &= ~FOREGROUND_ALL;
171+
attr |= (plain_attr & FOREGROUND_ALL);
172+
break;
173+
case 40: /* Black */
174+
attr &= ~BACKGROUND_ALL;
175+
break;
176+
case 41: /* Red */
177+
attr &= ~BACKGROUND_ALL;
178+
attr |= BACKGROUND_RED;
179+
break;
180+
case 42: /* Green */
181+
attr &= ~BACKGROUND_ALL;
182+
attr |= BACKGROUND_GREEN;
183+
break;
184+
case 43: /* Yellow */
185+
attr &= ~BACKGROUND_ALL;
186+
attr |= BACKGROUND_RED | BACKGROUND_GREEN;
187+
break;
188+
case 44: /* Blue */
189+
attr &= ~BACKGROUND_ALL;
190+
attr |= BACKGROUND_BLUE;
191+
break;
192+
case 45: /* Magenta */
193+
attr &= ~BACKGROUND_ALL;
194+
attr |= BACKGROUND_RED | BACKGROUND_BLUE;
195+
break;
196+
case 46: /* Cyan */
197+
attr &= ~BACKGROUND_ALL;
198+
attr |= BACKGROUND_GREEN | BACKGROUND_BLUE;
199+
break;
200+
case 47: /* White */
201+
attr |= BACKGROUND_RED |
202+
BACKGROUND_GREEN |
203+
BACKGROUND_BLUE;
204+
break;
205+
case 48: /* Unknown */
206+
break;
207+
case 49: /* reset */
208+
attr &= ~BACKGROUND_ALL;
209+
attr |= (plain_attr & BACKGROUND_ALL);
210+
break;
211+
default:
212+
/* Unsupported code */
213+
break;
214+
}
215+
str++;
216+
} while (*(str-1) == ';');
217+
218+
set_console_attr();
219+
break;
220+
case 'K':
221+
/* TODO */
222+
break;
223+
default:
224+
/* Unsupported code */
225+
break;
226+
}
227+
228+
return func + 1;
229+
}
230+
231+
static int ansi_emulate(const char *str, FILE *stream)
232+
{
233+
int rv = 0;
234+
const char *pos = str;
235+
236+
while (*pos) {
237+
pos = strstr(str, "\033[");
238+
if (pos) {
239+
size_t len = pos - str;
240+
241+
if (len) {
242+
size_t out_len = fwrite(str, 1, len, stream);
243+
rv += out_len;
244+
if (out_len < len)
245+
return rv;
246+
}
247+
248+
str = pos + 2;
249+
rv += 2;
250+
251+
fflush(stream);
252+
253+
pos = set_attr(str);
254+
rv += pos - str;
255+
str = pos;
256+
} else {
257+
rv += strlen(str);
258+
fputs(str, stream);
259+
return rv;
260+
}
261+
}
262+
return rv;
263+
}
264+
265+
int winansi_fputs(const char *str, FILE *stream)
266+
{
267+
int rv;
268+
269+
if (!isatty(fileno(stream)))
270+
return fputs(str, stream);
271+
272+
init();
273+
274+
if (!console)
275+
return fputs(str, stream);
276+
277+
rv = ansi_emulate(str, stream);
278+
279+
if (rv >= 0)
280+
return 0;
281+
else
282+
return EOF;
283+
}
284+
285+
static int winansi_vfprintf(FILE *stream, const char *format, va_list list)
286+
{
287+
int len, rv;
288+
char small_buf[256];
289+
char *buf = small_buf;
290+
va_list cp;
291+
292+
if (!isatty(fileno(stream)))
293+
goto abort;
294+
295+
init();
296+
297+
if (!console)
298+
goto abort;
299+
300+
va_copy(cp, list);
301+
len = vsnprintf(small_buf, sizeof(small_buf), format, cp);
302+
va_end(cp);
303+
304+
if (len > sizeof(small_buf) - 1) {
305+
buf = malloc(len + 1);
306+
if (!buf)
307+
goto abort;
308+
309+
len = vsnprintf(buf, len + 1, format, list);
310+
}
311+
312+
rv = ansi_emulate(buf, stream);
313+
314+
if (buf != small_buf)
315+
free(buf);
316+
return rv;
317+
318+
abort:
319+
rv = vfprintf(stream, format, list);
320+
return rv;
321+
}
322+
323+
int winansi_fprintf(FILE *stream, const char *format, ...)
324+
{
325+
va_list list;
326+
int rv;
327+
328+
va_start(list, format);
329+
rv = winansi_vfprintf(stream, format, list);
330+
va_end(list);
331+
332+
return rv;
333+
}
334+
335+
int winansi_printf(const char *format, ...)
336+
{
337+
va_list list;
338+
int rv;
339+
340+
va_start(list, format);
341+
rv = winansi_vfprintf(stdout, format, list);
342+
va_end(list);
343+
344+
return rv;
345+
}

0 commit comments

Comments
 (0)