From c09df8a74e8b5e106ad853cbb1e52f36b3663386 Mon Sep 17 00:00:00 2001 From: Peter Harris Date: Fri, 18 Jul 2008 09:34:44 +0200 Subject: [PATCH] Add ANSI control code emulation for the Windows console This adds only the minimum necessary to keep git pull/merge's diffstat from wrapping. Notably absent is support for the K (erase) operation, and support for POSIX write. Signed-off-by: Peter Harris Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- Makefile | 2 +- compat/mingw.h | 11 ++ compat/winansi.c | 345 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 357 insertions(+), 1 deletion(-) create mode 100644 compat/winansi.c diff --git a/Makefile b/Makefile index c01345e784..2b670d7845 100644 --- a/Makefile +++ b/Makefile @@ -741,7 +741,7 @@ ifneq (,$(findstring MINGW,$(uname_S))) COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1 COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\" - COMPAT_OBJS += compat/mingw.o compat/fnmatch.o compat/regex.o + COMPAT_OBJS += compat/mingw.o compat/fnmatch.o compat/regex.o compat/winansi.o EXTLIBS += -lws2_32 X = .exe template_dir = ../share/git-core/templates/ diff --git a/compat/mingw.h b/compat/mingw.h index 5a3bcee29b..8ffec51e73 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -193,6 +193,17 @@ static inline unsigned int git_ntohl(unsigned int x) sig_handler_t mingw_signal(int sig, sig_handler_t handler); #define signal mingw_signal +/* + * ANSI emulation wrappers + */ + +int winansi_fputs(const char *str, FILE *stream); +int winansi_printf(const char *format, ...) __attribute__((format (printf, 1, 2))); +int winansi_fprintf(FILE *stream, const char *format, ...) __attribute__((format (printf, 2, 3))); +#define fputs winansi_fputs +#define printf(...) winansi_printf(__VA_ARGS__) +#define fprintf(...) winansi_fprintf(__VA_ARGS__) + /* * git specific compatibility */ diff --git a/compat/winansi.c b/compat/winansi.c new file mode 100644 index 0000000000..e2d96dfe6f --- /dev/null +++ b/compat/winansi.c @@ -0,0 +1,345 @@ +/* + * Copyright 2008 Peter Harris + */ + +#include +#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; +}