Merge branch 'jk/head-symref'
[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  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 }