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