Merge branch 'bc/receive-pack-stdout-protection'
[git] / compat / winansi.c
1 /*
2  * Copyright 2008 Peter Harris <git@peter.is-a-geek.org>
3  */
4
5 #include "../git-compat-util.h"
6
7 /*
8  Functions to be wrapped:
9 */
10 #undef printf
11 #undef fprintf
12 #undef fputs
13 /* TODO: write */
14
15 /*
16  ANSI codes used by git: m, K
17
18  This file is git-specific. Therefore, this file does not attempt
19  to implement any codes that are not used by git.
20 */
21
22 static HANDLE console;
23 static WORD plain_attr;
24 static WORD attr;
25 static int negative;
26
27 static void init(void)
28 {
29         CONSOLE_SCREEN_BUFFER_INFO sbi;
30
31         static int initialized = 0;
32         if (initialized)
33                 return;
34
35         console = GetStdHandle(STD_OUTPUT_HANDLE);
36         if (console == INVALID_HANDLE_VALUE)
37                 console = NULL;
38
39         if (!console)
40                 return;
41
42         GetConsoleScreenBufferInfo(console, &sbi);
43         attr = plain_attr = sbi.wAttributes;
44         negative = 0;
45
46         initialized = 1;
47 }
48
49
50 #define FOREGROUND_ALL (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
51 #define BACKGROUND_ALL (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)
52
53 static void set_console_attr(void)
54 {
55         WORD attributes = attr;
56         if (negative) {
57                 attributes &= ~FOREGROUND_ALL;
58                 attributes &= ~BACKGROUND_ALL;
59
60                 /* This could probably use a bitmask
61                    instead of a series of ifs */
62                 if (attr & FOREGROUND_RED)
63                         attributes |= BACKGROUND_RED;
64                 if (attr & FOREGROUND_GREEN)
65                         attributes |= BACKGROUND_GREEN;
66                 if (attr & FOREGROUND_BLUE)
67                         attributes |= BACKGROUND_BLUE;
68
69                 if (attr & BACKGROUND_RED)
70                         attributes |= FOREGROUND_RED;
71                 if (attr & BACKGROUND_GREEN)
72                         attributes |= FOREGROUND_GREEN;
73                 if (attr & BACKGROUND_BLUE)
74                         attributes |= FOREGROUND_BLUE;
75         }
76         SetConsoleTextAttribute(console, attributes);
77 }
78
79 static void erase_in_line(void)
80 {
81         CONSOLE_SCREEN_BUFFER_INFO sbi;
82         DWORD dummy; /* Needed for Windows 7 (or Vista) regression */
83
84         if (!console)
85                 return;
86
87         GetConsoleScreenBufferInfo(console, &sbi);
88         FillConsoleOutputCharacterA(console, ' ',
89                 sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition,
90                 &dummy);
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 }