| /* | 
 |  * 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; | 
 | } |