Merge branch 'maint'
[git/ljr.git] / compat / winansi.c
blob9217c24b43f7546815079d540304aa71821df926
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 /* TODO: 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 void erase_in_line(void)
82 CONSOLE_SCREEN_BUFFER_INFO sbi;
83 DWORD dummy; /* Needed for Windows 7 (or Vista) regression */
85 if (!console)
86 return;
88 GetConsoleScreenBufferInfo(console, &sbi);
89 FillConsoleOutputCharacterA(console, ' ',
90 sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition,
91 &dummy);
95 static const char *set_attr(const char *str)
97 const char *func;
98 size_t len = strspn(str, "0123456789;");
99 func = str + len;
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;
228 str++;
229 } while (*(str-1) == ';');
231 set_console_attr();
232 break;
233 case 'K':
234 erase_in_line();
235 break;
236 default:
237 /* Unsupported code */
238 break;
241 return func + 1;
244 static int ansi_emulate(const char *str, FILE *stream)
246 int rv = 0;
247 const char *pos = str;
249 while (*pos) {
250 pos = strstr(str, "\033[");
251 if (pos) {
252 size_t len = pos - str;
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;
261 str = pos + 2;
262 rv += 2;
264 fflush(stream);
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;
275 return rv;
278 int winansi_fputs(const char *str, FILE *stream)
280 int rv;
282 if (!isatty(fileno(stream)))
283 return fputs(str, stream);
285 init();
287 if (!console)
288 return fputs(str, stream);
290 rv = ansi_emulate(str, stream);
292 if (rv >= 0)
293 return 0;
294 else
295 return EOF;
298 static int winansi_vfprintf(FILE *stream, const char *format, va_list list)
300 int len, rv;
301 char small_buf[256];
302 char *buf = small_buf;
303 va_list cp;
305 if (!isatty(fileno(stream)))
306 goto abort;
308 init();
310 if (!console)
311 goto abort;
313 va_copy(cp, list);
314 len = vsnprintf(small_buf, sizeof(small_buf), format, cp);
315 va_end(cp);
317 if (len > sizeof(small_buf) - 1) {
318 buf = malloc(len + 1);
319 if (!buf)
320 goto abort;
322 len = vsnprintf(buf, len + 1, format, list);
325 rv = ansi_emulate(buf, stream);
327 if (buf != small_buf)
328 free(buf);
329 return rv;
331 abort:
332 rv = vfprintf(stream, format, list);
333 return rv;
336 int winansi_fprintf(FILE *stream, const char *format, ...)
338 va_list list;
339 int rv;
341 va_start(list, format);
342 rv = winansi_vfprintf(stream, format, list);
343 va_end(list);
345 return rv;
348 int winansi_printf(const char *format, ...)
350 va_list list;
351 int rv;
353 va_start(list, format);
354 rv = winansi_vfprintf(stdout, format, list);
355 va_end(list);
357 return rv;