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