]>
Commit | Line | Data |
---|---|---|
c09df8a7 PH |
1 | /* |
2 | * Copyright 2008 Peter Harris <git@peter.is-a-geek.org> | |
3 | */ | |
4 | ||
c09df8a7 PH |
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. | |
c09df8a7 PH |
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 | ||
1897713f JS |
79 | static void erase_in_line(void) |
80 | { | |
81 | CONSOLE_SCREEN_BUFFER_INFO sbi; | |
492f7091 | 82 | DWORD dummy; /* Needed for Windows 7 (or Vista) regression */ |
1897713f JS |
83 | |
84 | if (!console) | |
85 | return; | |
86 | ||
87 | GetConsoleScreenBufferInfo(console, &sbi); | |
88 | FillConsoleOutputCharacterA(console, ' ', | |
89 | sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition, | |
492f7091 | 90 | &dummy); |
1897713f JS |
91 | } |
92 | ||
93 | ||
c09df8a7 PH |
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': | |
1897713f | 233 | erase_in_line(); |
c09df8a7 PH |
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 | } |