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