Windows ANSI - hook write()
[git/mingw/4msysgit/peterh.git] / compat / winansi.c
blob4266625dd2db71730fddc787485477c616e63d3c
1 /*
2 * Copyright 2008 Peter Harris <git@peter.is-a-geek.org>
3 */
5 #include <windows.h>
6 #include "../git-compat-util.h"
8 /*
9 Functions to be wrapped:
11 #undef printf
12 #undef fprintf
13 #undef fputs
14 #undef write
17 ANSI codes used by git: m, K
19 This file is git-specific. Therefore, this file does not attempt
20 to implement any codes that are not used by git.
23 static HANDLE console;
24 static WORD plain_attr;
25 static WORD attr;
26 static int negative;
28 static void init(void)
30 CONSOLE_SCREEN_BUFFER_INFO sbi;
32 static int initialized = 0;
33 if (initialized)
34 return;
36 console = GetStdHandle(STD_OUTPUT_HANDLE);
37 if (console == INVALID_HANDLE_VALUE)
38 console = NULL;
40 if (!console)
41 return;
43 GetConsoleScreenBufferInfo(console, &sbi);
44 attr = plain_attr = sbi.wAttributes;
45 negative = 0;
47 initialized = 1;
51 #define FOREGROUND_ALL (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
52 #define BACKGROUND_ALL (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)
54 static void set_console_attr(void)
56 WORD attributes = attr;
57 if (negative) {
58 attributes &= ~FOREGROUND_ALL;
59 attributes &= ~BACKGROUND_ALL;
61 /* This could probably use a bitmask
62 instead of a series of ifs */
63 if (attr & FOREGROUND_RED)
64 attributes |= BACKGROUND_RED;
65 if (attr & FOREGROUND_GREEN)
66 attributes |= BACKGROUND_GREEN;
67 if (attr & FOREGROUND_BLUE)
68 attributes |= BACKGROUND_BLUE;
70 if (attr & BACKGROUND_RED)
71 attributes |= FOREGROUND_RED;
72 if (attr & BACKGROUND_GREEN)
73 attributes |= FOREGROUND_GREEN;
74 if (attr & BACKGROUND_BLUE)
75 attributes |= FOREGROUND_BLUE;
77 SetConsoleTextAttribute(console, attributes);
80 static const char *set_attr(const char *str)
82 const char *func;
83 size_t len = strspn(str, "0123456789;");
84 func = str + len;
86 switch (*func) {
87 case 'm':
88 do {
89 long val = strtol(str, (char **)&str, 10);
90 switch (val) {
91 case 0: /* reset */
92 attr = plain_attr;
93 negative = 0;
94 break;
95 case 1: /* bold */
96 attr |= FOREGROUND_INTENSITY;
97 break;
98 case 2: /* faint */
99 case 22: /* normal */
100 attr &= ~FOREGROUND_INTENSITY;
101 break;
102 case 3: /* italic */
103 /* Unsupported */
104 break;
105 case 4: /* underline */
106 case 21: /* double underline */
107 /* Wikipedia says this flag does nothing */
108 /* Furthermore, mingw doesn't define this flag
109 attr |= COMMON_LVB_UNDERSCORE; */
110 break;
111 case 24: /* no underline */
112 /* attr &= ~COMMON_LVB_UNDERSCORE; */
113 break;
114 case 5: /* slow blink */
115 case 6: /* fast blink */
116 /* We don't have blink, but we do have
117 background intensity */
118 attr |= BACKGROUND_INTENSITY;
119 break;
120 case 25: /* no blink */
121 attr &= ~BACKGROUND_INTENSITY;
122 break;
123 case 7: /* negative */
124 negative = 1;
125 break;
126 case 27: /* positive */
127 negative = 0;
128 break;
129 case 8: /* conceal */
130 case 28: /* reveal */
131 /* Unsupported */
132 break;
133 case 30: /* Black */
134 attr &= ~FOREGROUND_ALL;
135 break;
136 case 31: /* Red */
137 attr &= ~FOREGROUND_ALL;
138 attr |= FOREGROUND_RED;
139 break;
140 case 32: /* Green */
141 attr &= ~FOREGROUND_ALL;
142 attr |= FOREGROUND_GREEN;
143 break;
144 case 33: /* Yellow */
145 attr &= ~FOREGROUND_ALL;
146 attr |= FOREGROUND_RED | FOREGROUND_GREEN;
147 break;
148 case 34: /* Blue */
149 attr &= ~FOREGROUND_ALL;
150 attr |= FOREGROUND_BLUE;
151 break;
152 case 35: /* Magenta */
153 attr &= ~FOREGROUND_ALL;
154 attr |= FOREGROUND_RED | FOREGROUND_BLUE;
155 break;
156 case 36: /* Cyan */
157 attr &= ~FOREGROUND_ALL;
158 attr |= FOREGROUND_GREEN | FOREGROUND_BLUE;
159 break;
160 case 37: /* White */
161 attr |= FOREGROUND_RED |
162 FOREGROUND_GREEN |
163 FOREGROUND_BLUE;
164 break;
165 case 38: /* Unknown */
166 break;
167 case 39: /* reset */
168 attr &= ~FOREGROUND_ALL;
169 attr |= (plain_attr & FOREGROUND_ALL);
170 break;
171 case 40: /* Black */
172 attr &= ~BACKGROUND_ALL;
173 break;
174 case 41: /* Red */
175 attr &= ~BACKGROUND_ALL;
176 attr |= BACKGROUND_RED;
177 break;
178 case 42: /* Green */
179 attr &= ~BACKGROUND_ALL;
180 attr |= BACKGROUND_GREEN;
181 break;
182 case 43: /* Yellow */
183 attr &= ~BACKGROUND_ALL;
184 attr |= BACKGROUND_RED | BACKGROUND_GREEN;
185 break;
186 case 44: /* Blue */
187 attr &= ~BACKGROUND_ALL;
188 attr |= BACKGROUND_BLUE;
189 break;
190 case 45: /* Magenta */
191 attr &= ~BACKGROUND_ALL;
192 attr |= BACKGROUND_RED | BACKGROUND_BLUE;
193 break;
194 case 46: /* Cyan */
195 attr &= ~BACKGROUND_ALL;
196 attr |= BACKGROUND_GREEN | BACKGROUND_BLUE;
197 break;
198 case 47: /* White */
199 attr |= BACKGROUND_RED |
200 BACKGROUND_GREEN |
201 BACKGROUND_BLUE;
202 break;
203 case 48: /* Unknown */
204 break;
205 case 49: /* reset */
206 attr &= ~BACKGROUND_ALL;
207 attr |= (plain_attr & BACKGROUND_ALL);
208 break;
209 default:
210 /* Unsupported code */
211 break;
213 str++;
214 } while (*(str-1) == ';');
216 set_console_attr();
217 break;
218 case 'K':
220 CONSOLE_SCREEN_BUFFER_INFO csbi;
221 COORD pos;
222 DWORD len;
223 long val = strtol(str, (char **)&str, 10);
225 if (!GetConsoleScreenBufferInfo(console, &csbi))
226 break;
228 switch (val) {
229 case 0:
230 /* Clear to end of line */
231 pos = csbi.dwCursorPosition;
232 len = csbi.dwSize.X - pos.X;
233 break;
234 case 1:
235 /* Clear to start of line */
236 pos.X = 0;
237 pos.Y = csbi.dwCursorPosition.Y;
238 len = csbi.dwCursorPosition.X - 1;
239 break;
240 case 2:
241 /* Clear entire line */
242 pos.X = 0;
243 pos.Y = csbi.dwCursorPosition.Y;
244 len = csbi.dwSize.X;
245 break;
246 default:
247 /* Unsupported code */
248 len = 0;
249 break;
252 if (len) {
253 FillConsoleOutputCharacter(console, ' ', len, pos, &len);
254 FillConsoleOutputAttribute(console, attr, len, pos, &len);
257 break;
259 default:
260 /* Unsupported code */
261 break;
264 return func + 1;
267 static int ansi_emulate(const char *str, FILE *stream)
269 int rv = 0;
270 const char *pos = str;
272 while (*pos) {
273 pos = strstr(str, "\033[");
274 if (pos) {
275 size_t len = pos - str;
277 if (len) {
278 size_t out_len = fwrite(str, 1, len, stream);
279 rv += out_len;
280 if (out_len < len)
281 return rv;
284 str = pos + 2;
285 rv += 2;
287 fflush(stream);
289 pos = set_attr(str);
290 rv += pos - str;
291 str = pos;
292 } else {
293 rv += strlen(str);
294 fputs(str, stream);
295 return rv;
298 return rv;
301 int winansi_fputs(const char *str, FILE *stream)
303 int rv;
305 if (!isatty(fileno(stream)))
306 return fputs(str, stream);
308 init();
310 if (!console)
311 return fputs(str, stream);
313 rv = ansi_emulate(str, stream);
315 if (rv >= 0)
316 return 0;
317 else
318 return EOF;
321 static int winansi_vfprintf(FILE *stream, const char *format, va_list list)
323 int len, rv;
324 char small_buf[256];
325 char *buf = small_buf;
326 va_list cp;
328 if (!isatty(fileno(stream)))
329 goto abort;
331 init();
333 if (!console)
334 goto abort;
336 va_copy(cp, list);
337 len = vsnprintf(small_buf, sizeof(small_buf), format, cp);
338 va_end(cp);
340 if (len > sizeof(small_buf) - 1) {
341 buf = malloc(len + 1);
342 if (!buf)
343 goto abort;
345 len = vsnprintf(buf, len + 1, format, list);
348 rv = ansi_emulate(buf, stream);
350 if (buf != small_buf)
351 free(buf);
352 return rv;
354 abort:
355 rv = vfprintf(stream, format, list);
356 return rv;
359 int winansi_fprintf(FILE *stream, const char *format, ...)
361 va_list list;
362 int rv;
364 va_start(list, format);
365 rv = winansi_vfprintf(stream, format, list);
366 va_end(list);
368 return rv;
371 int winansi_printf(const char *format, ...)
373 va_list list;
374 int rv;
376 va_start(list, format);
377 rv = winansi_vfprintf(stdout, format, list);
378 va_end(list);
380 return rv;
383 int winansi_write(int fd, const void *buf, size_t count)
385 int rv = 0;
386 const char *pos = buf;
388 init();
390 if (!console)
391 goto abort;
393 if (!isatty(fd))
394 goto abort;
396 while (count) {
397 pos = memchr(buf, '\033', count - 1);
398 while (pos && pos[1] != '[')
399 pos = memchr(pos + 1, '\033', count - (pos - (char *)buf) - 2);
401 if (pos) {
402 size_t len = pos - (const char *)buf;
404 if (len) {
405 int output_len = write(fd, buf, len);
406 if (output_len <= 0 && rv)
407 return rv;
408 if (output_len < len)
409 return rv + output_len;
412 pos = set_attr(pos + 2);
413 rv += pos - (const char *)buf;
414 count -= pos - (const char *)buf;
415 buf = pos;
416 } else {
417 int output_len;
418 output_len = write(fd, buf, count);
419 if (output_len > 0 || rv == 0)
420 rv += output_len;
421 return rv;
424 return rv;
426 abort:
427 return write(fd, buf, count);