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