| /* | |
| * Copyright 2008 Peter Harris <git@peter.is-a-geek.org> | |
| */ | |
| #include <windows.h> | |
| #include "../git-compat-util.h" | |
| /* | |
| Functions to be wrapped: | |
| */ | |
| #undef printf | |
| #undef fprintf | |
| #undef fputs | |
| /* TODO: write */ | |
| /* | |
| ANSI codes used by git: m, K | |
| This file is git-specific. Therefore, this file does not attempt | |
| to implement any codes that are not used by git. | |
| TODO: K | |
| */ | |
| static HANDLE console; | |
| static WORD plain_attr; | |
| static WORD attr; | |
| static int negative; | |
| static void init(void) | |
| { | |
| CONSOLE_SCREEN_BUFFER_INFO sbi; | |
| static int initialized = 0; | |
| if (initialized) | |
| return; | |
| console = GetStdHandle(STD_OUTPUT_HANDLE); | |
| if (console == INVALID_HANDLE_VALUE) | |
| console = NULL; | |
| if (!console) | |
| return; | |
| GetConsoleScreenBufferInfo(console, &sbi); | |
| attr = plain_attr = sbi.wAttributes; | |
| negative = 0; | |
| initialized = 1; | |
| } | |
| #define FOREGROUND_ALL (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE) | |
| #define BACKGROUND_ALL (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE) | |
| static void set_console_attr(void) | |
| { | |
| WORD attributes = attr; | |
| if (negative) { | |
| attributes &= ~FOREGROUND_ALL; | |
| attributes &= ~BACKGROUND_ALL; | |
| /* This could probably use a bitmask | |
| instead of a series of ifs */ | |
| if (attr & FOREGROUND_RED) | |
| attributes |= BACKGROUND_RED; | |
| if (attr & FOREGROUND_GREEN) | |
| attributes |= BACKGROUND_GREEN; | |
| if (attr & FOREGROUND_BLUE) | |
| attributes |= BACKGROUND_BLUE; | |
| if (attr & BACKGROUND_RED) | |
| attributes |= FOREGROUND_RED; | |
| if (attr & BACKGROUND_GREEN) | |
| attributes |= FOREGROUND_GREEN; | |
| if (attr & BACKGROUND_BLUE) | |
| attributes |= FOREGROUND_BLUE; | |
| } | |
| SetConsoleTextAttribute(console, attributes); | |
| } | |
| static const char *set_attr(const char *str) | |
| { | |
| const char *func; | |
| size_t len = strspn(str, "0123456789;"); | |
| func = str + len; | |
| switch (*func) { | |
| case 'm': | |
| do { | |
| long val = strtol(str, (char **)&str, 10); | |
| switch (val) { | |
| case 0: /* reset */ | |
| attr = plain_attr; | |
| negative = 0; | |
| break; | |
| case 1: /* bold */ | |
| attr |= FOREGROUND_INTENSITY; | |
| break; | |
| case 2: /* faint */ | |
| case 22: /* normal */ | |
| attr &= ~FOREGROUND_INTENSITY; | |
| break; | |
| case 3: /* italic */ | |
| /* Unsupported */ | |
| break; | |
| case 4: /* underline */ | |
| case 21: /* double underline */ | |
| /* Wikipedia says this flag does nothing */ | |
| /* Furthermore, mingw doesn't define this flag | |
| attr |= COMMON_LVB_UNDERSCORE; */ | |
| break; | |
| case 24: /* no underline */ | |
| /* attr &= ~COMMON_LVB_UNDERSCORE; */ | |
| break; | |
| case 5: /* slow blink */ | |
| case 6: /* fast blink */ | |
| /* We don't have blink, but we do have | |
| background intensity */ | |
| attr |= BACKGROUND_INTENSITY; | |
| break; | |
| case 25: /* no blink */ | |
| attr &= ~BACKGROUND_INTENSITY; | |
| break; | |
| case 7: /* negative */ | |
| negative = 1; | |
| break; | |
| case 27: /* positive */ | |
| negative = 0; | |
| break; | |
| case 8: /* conceal */ | |
| case 28: /* reveal */ | |
| /* Unsupported */ | |
| break; | |
| case 30: /* Black */ | |
| attr &= ~FOREGROUND_ALL; | |
| break; | |
| case 31: /* Red */ | |
| attr &= ~FOREGROUND_ALL; | |
| attr |= FOREGROUND_RED; | |
| break; | |
| case 32: /* Green */ | |
| attr &= ~FOREGROUND_ALL; | |
| attr |= FOREGROUND_GREEN; | |
| break; | |
| case 33: /* Yellow */ | |
| attr &= ~FOREGROUND_ALL; | |
| attr |= FOREGROUND_RED | FOREGROUND_GREEN; | |
| break; | |
| case 34: /* Blue */ | |
| attr &= ~FOREGROUND_ALL; | |
| attr |= FOREGROUND_BLUE; | |
| break; | |
| case 35: /* Magenta */ | |
| attr &= ~FOREGROUND_ALL; | |
| attr |= FOREGROUND_RED | FOREGROUND_BLUE; | |
| break; | |
| case 36: /* Cyan */ | |
| attr &= ~FOREGROUND_ALL; | |
| attr |= FOREGROUND_GREEN | FOREGROUND_BLUE; | |
| break; | |
| case 37: /* White */ | |
| attr |= FOREGROUND_RED | | |
| FOREGROUND_GREEN | | |
| FOREGROUND_BLUE; | |
| break; | |
| case 38: /* Unknown */ | |
| break; | |
| case 39: /* reset */ | |
| attr &= ~FOREGROUND_ALL; | |
| attr |= (plain_attr & FOREGROUND_ALL); | |
| break; | |
| case 40: /* Black */ | |
| attr &= ~BACKGROUND_ALL; | |
| break; | |
| case 41: /* Red */ | |
| attr &= ~BACKGROUND_ALL; | |
| attr |= BACKGROUND_RED; | |
| break; | |
| case 42: /* Green */ | |
| attr &= ~BACKGROUND_ALL; | |
| attr |= BACKGROUND_GREEN; | |
| break; | |
| case 43: /* Yellow */ | |
| attr &= ~BACKGROUND_ALL; | |
| attr |= BACKGROUND_RED | BACKGROUND_GREEN; | |
| break; | |
| case 44: /* Blue */ | |
| attr &= ~BACKGROUND_ALL; | |
| attr |= BACKGROUND_BLUE; | |
| break; | |
| case 45: /* Magenta */ | |
| attr &= ~BACKGROUND_ALL; | |
| attr |= BACKGROUND_RED | BACKGROUND_BLUE; | |
| break; | |
| case 46: /* Cyan */ | |
| attr &= ~BACKGROUND_ALL; | |
| attr |= BACKGROUND_GREEN | BACKGROUND_BLUE; | |
| break; | |
| case 47: /* White */ | |
| attr |= BACKGROUND_RED | | |
| BACKGROUND_GREEN | | |
| BACKGROUND_BLUE; | |
| break; | |
| case 48: /* Unknown */ | |
| break; | |
| case 49: /* reset */ | |
| attr &= ~BACKGROUND_ALL; | |
| attr |= (plain_attr & BACKGROUND_ALL); | |
| break; | |
| default: | |
| /* Unsupported code */ | |
| break; | |
| } | |
| str++; | |
| } while (*(str-1) == ';'); | |
| set_console_attr(); | |
| break; | |
| case 'K': | |
| /* TODO */ | |
| break; | |
| default: | |
| /* Unsupported code */ | |
| break; | |
| } | |
| return func + 1; | |
| } | |
| static int ansi_emulate(const char *str, FILE *stream) | |
| { | |
| int rv = 0; | |
| const char *pos = str; | |
| while (*pos) { | |
| pos = strstr(str, "\033["); | |
| if (pos) { | |
| size_t len = pos - str; | |
| if (len) { | |
| size_t out_len = fwrite(str, 1, len, stream); | |
| rv += out_len; | |
| if (out_len < len) | |
| return rv; | |
| } | |
| str = pos + 2; | |
| rv += 2; | |
| fflush(stream); | |
| pos = set_attr(str); | |
| rv += pos - str; | |
| str = pos; | |
| } else { | |
| rv += strlen(str); | |
| fputs(str, stream); | |
| return rv; | |
| } | |
| } | |
| return rv; | |
| } | |
| int winansi_fputs(const char *str, FILE *stream) | |
| { | |
| int rv; | |
| if (!isatty(fileno(stream))) | |
| return fputs(str, stream); | |
| init(); | |
| if (!console) | |
| return fputs(str, stream); | |
| rv = ansi_emulate(str, stream); | |
| if (rv >= 0) | |
| return 0; | |
| else | |
| return EOF; | |
| } | |
| static int winansi_vfprintf(FILE *stream, const char *format, va_list list) | |
| { | |
| int len, rv; | |
| char small_buf[256]; | |
| char *buf = small_buf; | |
| va_list cp; | |
| if (!isatty(fileno(stream))) | |
| goto abort; | |
| init(); | |
| if (!console) | |
| goto abort; | |
| va_copy(cp, list); | |
| len = vsnprintf(small_buf, sizeof(small_buf), format, cp); | |
| va_end(cp); | |
| if (len > sizeof(small_buf) - 1) { | |
| buf = malloc(len + 1); | |
| if (!buf) | |
| goto abort; | |
| len = vsnprintf(buf, len + 1, format, list); | |
| } | |
| rv = ansi_emulate(buf, stream); | |
| if (buf != small_buf) | |
| free(buf); | |
| return rv; | |
| abort: | |
| rv = vfprintf(stream, format, list); | |
| return rv; | |
| } | |
| int winansi_fprintf(FILE *stream, const char *format, ...) | |
| { | |
| va_list list; | |
| int rv; | |
| va_start(list, format); | |
| rv = winansi_vfprintf(stream, format, list); | |
| va_end(list); | |
| return rv; | |
| } | |
| int winansi_printf(const char *format, ...) | |
| { | |
| va_list list; | |
| int rv; | |
| va_start(list, format); | |
| rv = winansi_vfprintf(stdout, format, list); | |
| va_end(list); | |
| return rv; | |
| } |