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
+}