From c09df8a74e8b5e106ad853cbb1e52f36b3663386 Mon Sep 17 00:00:00 2001 From: Peter Harris Date: Fri, 18 Jul 2008 09:34:44 +0200 Subject: 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 --- compat/winansi.c | 345 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 345 insertions(+) create mode 100644 compat/winansi.c (limited to 'compat/winansi.c') 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; +} -- cgit v1.2.3 From 1897713fbd96229f3581b8e8a5653da917882195 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 10 Mar 2009 22:58:09 +0100 Subject: winansi: support ESC [ K (erase in line) Signed-off-by: Johannes Schindelin Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- compat/winansi.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'compat/winansi.c') diff --git a/compat/winansi.c b/compat/winansi.c index e2d96dfe6f..44dc293ad3 100644 --- a/compat/winansi.c +++ b/compat/winansi.c @@ -18,8 +18,6 @@ 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; @@ -79,6 +77,20 @@ static void set_console_attr(void) SetConsoleTextAttribute(console, attributes); } +static void erase_in_line(void) +{ + CONSOLE_SCREEN_BUFFER_INFO sbi; + + if (!console) + return; + + GetConsoleScreenBufferInfo(console, &sbi); + FillConsoleOutputCharacterA(console, ' ', + sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition, + NULL); +} + + static const char *set_attr(const char *str) { const char *func; @@ -218,7 +230,7 @@ static const char *set_attr(const char *str) set_console_attr(); break; case 'K': - /* TODO */ + erase_in_line(); break; default: /* Unsupported code */ -- cgit v1.2.3 From 492f70913e11de19bc4cb197b1fe195c645b89c9 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 1 Jun 2009 08:04:16 +0200 Subject: Work around a regression in Windows 7, causing erase_in_line() to crash sometimes The function FillConsoleOutputCharacterA() was pretty content in XP to take a NULL pointer if we did not want to store the number of written columns. In Windows 7, it crashes, but only when called from within Git Bash, not from within cmd.exe. Go figure. Signed-off-by: Johannes Schindelin Signed-off-by: Steffen Prohaska Signed-off-by: Junio C Hamano --- compat/winansi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'compat/winansi.c') diff --git a/compat/winansi.c b/compat/winansi.c index 44dc293ad3..9217c24b43 100644 --- a/compat/winansi.c +++ b/compat/winansi.c @@ -80,6 +80,7 @@ static void set_console_attr(void) static void erase_in_line(void) { CONSOLE_SCREEN_BUFFER_INFO sbi; + DWORD dummy; /* Needed for Windows 7 (or Vista) regression */ if (!console) return; @@ -87,7 +88,7 @@ static void erase_in_line(void) GetConsoleScreenBufferInfo(console, &sbi); FillConsoleOutputCharacterA(console, ' ', sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition, - NULL); + &dummy); } -- cgit v1.2.3 From 435bdf8c7ffa493f8f6f2e8f329f8cc22db16ce6 Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Wed, 16 Sep 2009 10:20:26 +0200 Subject: Make usage of windows.h lean and mean Centralize the include of windows.h in git-compat-util.h, turn on WIN32_LEAN_AND_MEAN to avoid including plenty of other header files which is not needed in Git. Also ensure we load winsock2.h first, so we don't load the older winsock definitions at a later stage, since they contain duplicate definitions. When moving windows.h into git-compat-util.h, we need to protect the definition of struct pollfd in mingw.h, since this file is used by both MinGW and MSVC, and the latter defines this struct in winsock2.h. We need to keep the windows.h include in compat/win32.h, since its shared by both MinGW and Cygwin, and we're not touching Cygwin in this commit. The include in git-compat-util.h is protected with an ifdef WIN32, which is not the case when compiling for Cygwin. Signed-off-by: Marius Storm-Olsen Acked-by: Johannes Sixt Signed-off-by: Junio C Hamano --- compat/winansi.c | 1 - 1 file changed, 1 deletion(-) (limited to 'compat/winansi.c') diff --git a/compat/winansi.c b/compat/winansi.c index 9217c24b43..dedce2104e 100644 --- a/compat/winansi.c +++ b/compat/winansi.c @@ -2,7 +2,6 @@ * Copyright 2008 Peter Harris */ -#include #include "../git-compat-util.h" /* -- cgit v1.2.3