summaryrefslogtreecommitdiff
path: root/win32/winansi.c
diff options
context:
space:
mode:
Diffstat (limited to 'win32/winansi.c')
-rw-r--r--win32/winansi.c685
1 files changed, 685 insertions, 0 deletions
diff --git a/win32/winansi.c b/win32/winansi.c
new file mode 100644
index 000000000..c47e29a28
--- /dev/null
+++ b/win32/winansi.c
@@ -0,0 +1,685 @@
1/*
2 * Copyright 2008 Peter Harris <git@peter.is-a-geek.org>
3 */
4
5#include "libbb.h"
6#include <windows.h>
7#undef PACKED
8
9/*
10 Functions to be wrapped:
11*/
12#undef vprintf
13#undef printf
14#undef fprintf
15#undef fputs
16#undef putchar
17#undef fwrite
18#undef puts
19#undef write
20#undef read
21#undef getc
22
23/*
24 ANSI codes used by git: m, K
25
26 This file is git-specific. Therefore, this file does not attempt
27 to implement any codes that are not used by git.
28*/
29
30static HANDLE console;
31static HANDLE console_in;
32static WORD plain_attr;
33static WORD attr;
34static int negative;
35
36static void init(void)
37{
38 CONSOLE_SCREEN_BUFFER_INFO sbi;
39
40 static int initialized = 0;
41 if (initialized)
42 return;
43
44 console_in = GetStdHandle(STD_INPUT_HANDLE);
45 if (console_in == INVALID_HANDLE_VALUE)
46 console_in = NULL;
47
48 console = GetStdHandle(STD_OUTPUT_HANDLE);
49 if (console == INVALID_HANDLE_VALUE)
50 console = NULL;
51
52 if (!console)
53 return;
54
55 GetConsoleScreenBufferInfo(console, &sbi);
56 attr = plain_attr = sbi.wAttributes;
57 negative = 0;
58
59 initialized = 1;
60}
61
62
63#define FOREGROUND_ALL (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
64#define BACKGROUND_ALL (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)
65
66static void set_console_attr(void)
67{
68 WORD attributes = attr;
69 if (negative) {
70 attributes &= ~FOREGROUND_ALL;
71 attributes &= ~BACKGROUND_ALL;
72
73 /* This could probably use a bitmask
74 instead of a series of ifs */
75 if (attr & FOREGROUND_RED)
76 attributes |= BACKGROUND_RED;
77 if (attr & FOREGROUND_GREEN)
78 attributes |= BACKGROUND_GREEN;
79 if (attr & FOREGROUND_BLUE)
80 attributes |= BACKGROUND_BLUE;
81
82 if (attr & BACKGROUND_RED)
83 attributes |= FOREGROUND_RED;
84 if (attr & BACKGROUND_GREEN)
85 attributes |= FOREGROUND_GREEN;
86 if (attr & BACKGROUND_BLUE)
87 attributes |= FOREGROUND_BLUE;
88 }
89 SetConsoleTextAttribute(console, attributes);
90}
91
92static void erase_in_line(void)
93{
94 CONSOLE_SCREEN_BUFFER_INFO sbi;
95 DWORD dummy; /* Needed for Windows 7 (or Vista) regression */
96
97 if (!console)
98 return;
99
100 GetConsoleScreenBufferInfo(console, &sbi);
101 FillConsoleOutputCharacterA(console, ' ',
102 sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition,
103 &dummy);
104}
105
106static void erase_till_end_of_screen(void)
107{
108 CONSOLE_SCREEN_BUFFER_INFO sbi;
109 COORD pos;
110 DWORD dummy;
111
112 if (!console)
113 return;
114
115 GetConsoleScreenBufferInfo(console, &sbi);
116 FillConsoleOutputCharacterA(console, ' ',
117 sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition,
118 &dummy);
119
120 pos.X = 0;
121 for (pos.Y = sbi.dwCursorPosition.Y+1; pos.Y < sbi.dwSize.Y; pos.Y++)
122 FillConsoleOutputCharacterA(console, ' ', sbi.dwSize.X,
123 pos, &dummy);
124}
125
126static void move_cursor_row(int n)
127{
128 CONSOLE_SCREEN_BUFFER_INFO sbi;
129
130 if (!console)
131 return;
132
133 GetConsoleScreenBufferInfo(console, &sbi);
134 sbi.dwCursorPosition.Y += n;
135 SetConsoleCursorPosition(console, sbi.dwCursorPosition);
136}
137
138static void move_cursor_column(int n)
139{
140 CONSOLE_SCREEN_BUFFER_INFO sbi;
141
142 if (!console)
143 return;
144
145 GetConsoleScreenBufferInfo(console, &sbi);
146 sbi.dwCursorPosition.X += n;
147 SetConsoleCursorPosition(console, sbi.dwCursorPosition);
148}
149
150static void move_cursor(int x, int y)
151{
152 COORD pos;
153
154 if (!console)
155 return;
156
157 pos.X = x;
158 pos.Y = y;
159 SetConsoleCursorPosition(console, pos);
160}
161
162static const char *set_attr(const char *str)
163{
164 const char *func;
165 size_t len = strspn(str, "0123456789;");
166 func = str + len;
167
168 switch (*func) {
169 case 'm':
170 do {
171 long val = strtol(str, (char **)&str, 10);
172 switch (val) {
173 case 0: /* reset */
174 attr = plain_attr;
175 negative = 0;
176 break;
177 case 1: /* bold */
178 attr |= FOREGROUND_INTENSITY;
179 break;
180 case 2: /* faint */
181 case 22: /* normal */
182 attr &= ~FOREGROUND_INTENSITY;
183 break;
184 case 3: /* italic */
185 /* Unsupported */
186 break;
187 case 4: /* underline */
188 case 21: /* double underline */
189 /* Wikipedia says this flag does nothing */
190 /* Furthermore, mingw doesn't define this flag
191 attr |= COMMON_LVB_UNDERSCORE; */
192 break;
193 case 24: /* no underline */
194 /* attr &= ~COMMON_LVB_UNDERSCORE; */
195 break;
196 case 5: /* slow blink */
197 case 6: /* fast blink */
198 /* We don't have blink, but we do have
199 background intensity */
200 attr |= BACKGROUND_INTENSITY;
201 break;
202 case 25: /* no blink */
203 attr &= ~BACKGROUND_INTENSITY;
204 break;
205 case 7: /* negative */
206 negative = 1;
207 break;
208 case 27: /* positive */
209 negative = 0;
210 break;
211 case 8: /* conceal */
212 case 28: /* reveal */
213 /* Unsupported */
214 break;
215 case 30: /* Black */
216 attr &= ~FOREGROUND_ALL;
217 break;
218 case 31: /* Red */
219 attr &= ~FOREGROUND_ALL;
220 attr |= FOREGROUND_RED;
221 break;
222 case 32: /* Green */
223 attr &= ~FOREGROUND_ALL;
224 attr |= FOREGROUND_GREEN;
225 break;
226 case 33: /* Yellow */
227 attr &= ~FOREGROUND_ALL;
228 attr |= FOREGROUND_RED | FOREGROUND_GREEN;
229 break;
230 case 34: /* Blue */
231 attr &= ~FOREGROUND_ALL;
232 attr |= FOREGROUND_BLUE;
233 break;
234 case 35: /* Magenta */
235 attr &= ~FOREGROUND_ALL;
236 attr |= FOREGROUND_RED | FOREGROUND_BLUE;
237 break;
238 case 36: /* Cyan */
239 attr &= ~FOREGROUND_ALL;
240 attr |= FOREGROUND_GREEN | FOREGROUND_BLUE;
241 break;
242 case 37: /* White */
243 attr |= FOREGROUND_RED |
244 FOREGROUND_GREEN |
245 FOREGROUND_BLUE;
246 break;
247 case 38: /* Unknown */
248 break;
249 case 39: /* reset */
250 attr &= ~FOREGROUND_ALL;
251 attr |= (plain_attr & FOREGROUND_ALL);
252 break;
253 case 40: /* Black */
254 attr &= ~BACKGROUND_ALL;
255 break;
256 case 41: /* Red */
257 attr &= ~BACKGROUND_ALL;
258 attr |= BACKGROUND_RED;
259 break;
260 case 42: /* Green */
261 attr &= ~BACKGROUND_ALL;
262 attr |= BACKGROUND_GREEN;
263 break;
264 case 43: /* Yellow */
265 attr &= ~BACKGROUND_ALL;
266 attr |= BACKGROUND_RED | BACKGROUND_GREEN;
267 break;
268 case 44: /* Blue */
269 attr &= ~BACKGROUND_ALL;
270 attr |= BACKGROUND_BLUE;
271 break;
272 case 45: /* Magenta */
273 attr &= ~BACKGROUND_ALL;
274 attr |= BACKGROUND_RED | BACKGROUND_BLUE;
275 break;
276 case 46: /* Cyan */
277 attr &= ~BACKGROUND_ALL;
278 attr |= BACKGROUND_GREEN | BACKGROUND_BLUE;
279 break;
280 case 47: /* White */
281 attr |= BACKGROUND_RED |
282 BACKGROUND_GREEN |
283 BACKGROUND_BLUE;
284 break;
285 case 48: /* Unknown */
286 break;
287 case 49: /* reset */
288 attr &= ~BACKGROUND_ALL;
289 attr |= (plain_attr & BACKGROUND_ALL);
290 break;
291 default:
292 /* Unsupported code */
293 break;
294 }
295 str++;
296 } while (*(str-1) == ';');
297
298 set_console_attr();
299 break;
300 case 'A': /* up */
301 move_cursor_row(-strtol(str, (char **)&str, 10));
302 break;
303 case 'B': /* down */
304 move_cursor_row(strtol(str, (char **)&str, 10));
305 break;
306 case 'C': /* forward */
307 move_cursor_column(strtol(str, (char **)&str, 10));
308 break;
309 case 'D': /* back */
310 move_cursor_column(-strtol(str, (char **)&str, 10));
311 break;
312 case 'H':
313 if (!len)
314 move_cursor(0, 0);
315 else {
316 int row = strtol(str, (char **)&str, 10);
317 if (*str == ';') {
318 int col = strtol(str+1, (char **)&str, 10);
319 move_cursor(col-1, row-1);
320 }
321 }
322 break;
323 case 'J':
324 erase_till_end_of_screen();
325 break;
326 case 'K':
327 erase_in_line();
328 break;
329 case '?':
330 /* skip this to avoid ugliness when vi is shut down */
331 ++str;
332 while (isdigit(*str))
333 ++str;
334 func = str;
335 break;
336 default:
337 /* Unsupported code */
338 break;
339 }
340
341 return func + 1;
342}
343
344static int ansi_emulate(const char *s, FILE *stream)
345{
346 int rv = 0;
347 const char *t;
348 char *pos, *str;
349 size_t out_len, cur_len;
350 static size_t max_len = 0;
351 static char *mem = NULL;
352
353 /* if no special treatment is required output the string as-is */
354 for ( t=s; *t; ++t ) {
355 if ( *t == '\033' || *t > 0x7f ) {
356 break;
357 }
358 }
359
360 if ( *t == '\0' ) {
361 return fputs(s, stream) == EOF ? EOF : strlen(s);
362 }
363
364 /* make a writable copy of the string and retain it for reuse */
365 cur_len = strlen(s);
366 if ( cur_len == 0 || cur_len > max_len ) {
367 free(mem);
368 mem = strdup(s);
369 max_len = cur_len;
370 }
371 else {
372 strcpy(mem, s);
373 }
374 pos = str = mem;
375
376 while (*pos) {
377 pos = strstr(str, "\033[");
378 if (pos) {
379 size_t len = pos - str;
380
381 if (len) {
382 CharToOemBuff(str, str, len);
383 out_len = fwrite(str, 1, len, stream);
384 rv += out_len;
385 if (out_len < len)
386 return rv;
387 }
388
389 str = pos + 2;
390 rv += 2;
391
392 fflush(stream);
393
394 pos = (char *)set_attr(str);
395 rv += pos - str;
396 str = pos;
397 } else {
398 rv += strlen(str);
399 CharToOem(str, str);
400 fputs(str, stream);
401 return rv;
402 }
403 }
404 return rv;
405}
406
407int winansi_putchar(int c)
408{
409 char t = c;
410 char *s = &t;
411
412 if (!isatty(STDOUT_FILENO))
413 return putchar(c);
414
415 init();
416
417 if (!console)
418 return putchar(c);
419
420 CharToOemBuff(s, s, 1);
421 return putchar(t) == EOF ? EOF : c;
422}
423
424int winansi_puts(const char *s)
425{
426 int rv;
427
428 if (!isatty(STDOUT_FILENO))
429 return puts(s);
430
431 init();
432
433 if (!console)
434 return puts(s);
435
436 rv = ansi_emulate(s, stdout);
437 putchar('\n');
438
439 return rv;
440}
441
442size_t winansi_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
443{
444 size_t lsize, lmemb;
445 char *str;
446 int rv;
447
448 lsize = MIN(size, nmemb);
449 lmemb = MAX(size, nmemb);
450 if (lsize != 1)
451 return fwrite(ptr, size, nmemb, stream);
452
453 if (!isatty(fileno(stream)))
454 return fwrite(ptr, size, nmemb, stream);
455
456 init();
457
458 if (!console)
459 return fwrite(ptr, size, nmemb, stream);
460
461 str = xmalloc(lmemb+1);
462 memcpy(str, ptr, lmemb);
463 str[lmemb] = '\0';
464
465 rv = ansi_emulate(str, stream);
466 free(str);
467
468 return rv;
469}
470
471int winansi_fputs(const char *str, FILE *stream)
472{
473 int rv;
474
475 if (!isatty(fileno(stream)))
476 return fputs(str, stream);
477
478 init();
479
480 if (!console)
481 return fputs(str, stream);
482
483 rv = ansi_emulate(str, stream);
484
485 if (rv >= 0)
486 return 0;
487 else
488 return EOF;
489}
490
491int winansi_vfprintf(FILE *stream, const char *format, va_list list)
492{
493 int len, rv;
494 char small_buf[256];
495 char *buf = small_buf;
496 va_list cp;
497
498 if (!isatty(fileno(stream)))
499 goto abort;
500
501 init();
502
503 if (!console)
504 goto abort;
505
506 va_copy(cp, list);
507 len = vsnprintf(small_buf, sizeof(small_buf), format, cp);
508 va_end(cp);
509
510 if (len > sizeof(small_buf) - 1) {
511 buf = malloc(len + 1);
512 if (!buf)
513 goto abort;
514
515 len = vsnprintf(buf, len + 1, format, list);
516 }
517
518 rv = ansi_emulate(buf, stream);
519
520 if (buf != small_buf)
521 free(buf);
522 return rv;
523
524abort:
525 rv = vfprintf(stream, format, list);
526 return rv;
527}
528
529int winansi_fprintf(FILE *stream, const char *format, ...)
530{
531 va_list list;
532 int rv;
533
534 va_start(list, format);
535 rv = winansi_vfprintf(stream, format, list);
536 va_end(list);
537
538 return rv;
539}
540
541int winansi_printf(const char *format, ...)
542{
543 va_list list;
544 int rv;
545
546 va_start(list, format);
547 rv = winansi_vfprintf(stdout, format, list);
548 va_end(list);
549
550 return rv;
551}
552
553int winansi_get_terminal_width_height(struct winsize *win)
554{
555 BOOL ret;
556 CONSOLE_SCREEN_BUFFER_INFO sbi;
557
558 init();
559
560 win->ws_row = 0;
561 win->ws_col = 0;
562 if ((ret=GetConsoleScreenBufferInfo(console, &sbi)) != 0) {
563 win->ws_row = sbi.srWindow.Bottom - sbi.srWindow.Top + 1;
564 win->ws_col = sbi.srWindow.Right - sbi.srWindow.Left + 1;
565 }
566
567 return ret ? 0 : -1;
568}
569
570static int ansi_emulate_write(int fd, const void *buf, size_t count)
571{
572 int rv = 0, i;
573 const char *s = (const char *)buf;
574 char *pos, *str;
575 size_t len, out_len;
576 static size_t max_len = 0;
577 static char *mem = NULL;
578
579 /* if no special treatment is required output the string as-is */
580 for ( i=0; i<count; ++i ) {
581 if ( s[i] == '\033' || s[i] > 0x7f ) {
582 break;
583 }
584 }
585
586 if ( i == count ) {
587 return write(fd, buf, count);
588 }
589
590 /* make a writable copy of the data and retain it for reuse */
591 if ( count > max_len ) {
592 free(mem);
593 mem = malloc(count+1);
594 max_len = count;
595 }
596 memcpy(mem, buf, count);
597 mem[count] = '\0';
598 pos = str = mem;
599
600 /* we're writing to the console so we assume the data isn't binary */
601 while (*pos) {
602 pos = strstr(str, "\033[");
603 if (pos) {
604 len = pos - str;
605
606 if (len) {
607 CharToOemBuff(str, str, len);
608 out_len = write(fd, str, len);
609 rv += out_len;
610 if (out_len < len)
611 return rv;
612 }
613
614 str = pos + 2;
615 rv += 2;
616
617 pos = (char *)set_attr(str);
618 rv += pos - str;
619 str = pos;
620 } else {
621 len = strlen(str);
622 rv += len;
623 CharToOem(str, str);
624 write(fd, str, len);
625 return rv;
626 }
627 }
628 return rv;
629}
630
631int winansi_write(int fd, const void *buf, size_t count)
632{
633 if (!isatty(fd))
634 return write(fd, buf, count);
635
636 init();
637
638 if (!console)
639 return write(fd, buf, count);
640
641 return ansi_emulate_write(fd, buf, count);
642}
643
644int winansi_read(int fd, void *buf, size_t count)
645{
646 int rv;
647
648 rv = read(fd, buf, count);
649 if (!isatty(fd))
650 return rv;
651
652 init();
653
654 if (!console_in)
655 return rv;
656
657 if ( rv > 0 ) {
658 OemToCharBuff(buf, buf, rv);
659 }
660
661 return rv;
662}
663
664int winansi_getc(FILE *stream)
665{
666 int rv;
667
668 rv = getc(stream);
669 if (!isatty(fileno(stream)))
670 return rv;
671
672 init();
673
674 if (!console_in)
675 return rv;
676
677 if ( rv != EOF ) {
678 unsigned char c = (unsigned char)rv;
679 char *s = (char *)&c;
680 OemToCharBuff(s, s, 1);
681 rv = (int)c;
682 }
683
684 return rv;
685}