@matheusbsbs / matheus-bagliano / commits / c09df8a74e
Fork 0

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>

Peter Harris committed Jul 18, 2008 at 09:34 UTC c09df8a74e8b5e106ad853cbb1e52f36b3663386
3 files changed +357 -1
Makefile
+1 -1
@@ -741,7 +741,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
741 COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat
742 COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1
743 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
745 EXTLIBS += -lws2_32
746 X = .exe
747 template_dir = ../share/git-core/templates/
compat/mingw.h
+11
@@ -193,6 +193,17 @@ static inline unsigned int git_ntohl(unsigned int x)
193 sig_handler_t mingw_signal(int sig, sig_handler_t handler);
194 #define signal mingw_signal
195
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 +
207 /*
208 * git specific compatibility
209 */
compat/winansi.c new
+345
@@ -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 +}