Note that this does not interfere with rxvt's control code handling, since
rxvt fails the isatty() test, so this will pass the control codes through
unmodified.
Signed-off-by: Peter Harris <[EMAIL PROTECTED]>
---
I got tired of seeing control codes appear on my console. Rather than
turn off colours, I decided to fix them instead.
Note that this patch does not catch \033[K at the end of lines when
fetching from a remote. This patch only wraps ANSI C printf/fputs, not
POSIX open/write (yet).
I'm not sure if Cygwin wants to define WIN_ANSI=YesPlease too.
Comments welcome.
Makefile | 6 +
compat/winansi.c | 309 +++++++++++++++++++++++++++++++++++++++++++++++++++++
git-compat-util.h | 7 ++
3 files changed, 322 insertions(+), 0 deletions(-)
create mode 100644 compat/winansi.c
diff --git a/Makefile b/Makefile
index 5e57a58..62034e7 100644
--- a/Makefile
+++ b/Makefile
@@ -547,6 +547,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
NO_R_TO_GCC_LINKER = YesPlease
INTERNAL_QSORT = YesPlease
NO_EXTRA_PROGRAMS = YesPlease
+ WIN_ANSI = YesPlease
COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat
COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
COMPAT_OBJS += compat/mingw.o compat/fnmatch.o compat/regex.o
@@ -781,6 +782,11 @@ ifdef THREADED_DELTA_SEARCH
EXTLIBS += -lpthread
endif
+ifdef WIN_ANSI
+ COMPAT_CFLAGS += -DWIN_ANSI
+ COMPAT_OBJS += compat/winansi.o
+endif
+
ifeq ($(TCLTK_PATH),)
NO_TCLTK=NoThanks
endif
diff --git a/compat/winansi.c b/compat/winansi.c
new file mode 100644
index 0000000..86c3fd2
--- /dev/null
+++ b/compat/winansi.c
@@ -0,0 +1,309 @@
+#include <windows.h>
+#include "../git-compat-util.h"
+
+/*
+ Functions to be wrapped:
+*/
+#undef printf
+#undef fputs
+
+/*
+ ANSI codes to implement: m, 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 output_len = fwrite(str, 1, len, stream);
+ rv += output_len;
+ if (output_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 git_fputs(const char *str, FILE *stream)
+{
+ int rv;
+
+ init();
+
+ if (!console)
+ return fputs(str, stream);
+
+ if (!isatty(fileno(stream)))
+ return fputs(str, stream);
+
+ rv = ansi_emulate(str, stream);
+
+ if (rv >= 0)
+ return 0;
+ else
+ return EOF;
+}
+
+int git_printf(const char *format, ...)
+{
+ va_list list;
+
+ char small_buf[256];
+ char *buf = small_buf;
+ int len, rv;
+
+ init();
+
+ if (!console)
+ goto abort;
+
+ if (!isatty(fileno(stdout)))
+ goto abort;
+
+ va_start(list, format);
+ len = vsnprintf(small_buf, sizeof(small_buf), format, list);
+ va_end(list);
+
+ if (len > sizeof(small_buf) - 1) {
+ buf = malloc(len + 1);
+ if (!buf)
+ goto abort;
+
+ va_start(list, format);
+ len = vsnprintf(buf, len + 1, format, list);
+ va_end(list);
+ }
+
+ rv = ansi_emulate(buf, stdout);
+
+ if (buf != small_buf)
+ free(buf);
+ return rv;
+
+abort:
+ va_start(list, format);
+ rv = vprintf(format, list);
+ va_end(list);
+ return rv;
+}
diff --git a/git-compat-util.h b/git-compat-util.h
index e0fe165..218d6b6 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -661,4 +661,11 @@ void git_qsort(void *base, size_t nmemb, size_t size,
#define qsort git_qsort
#endif
+#ifdef WIN_ANSI
+extern int git_fputs(const char *str, FILE *stream);
+extern int git_printf(const char *format, ...) __attribute__((format (printf,
1, 2)));
+#define fputs git_fputs
+#define printf(...) git_printf(__VA_ARGS__)
+#endif
+
#endif
--
1.5.3.7