diff options
| author | 2021-06-11 01:37:27 +0200 | |
|---|---|---|
| committer | 2021-06-11 01:37:27 +0200 | |
| commit | 93bb30c682bdc984b9acced58e9a1268eefade15 (patch) | |
| tree | dff9e6157875b5b3e63015bd6cb6f8e9873ed1db | |
| parent | 0dcfb68954f7ee9957f528f0eea1f9dba9c9cb68 (diff) | |
| download | libimobiledevice-glue-93bb30c682bdc984b9acced58e9a1268eefade15.tar.gz libimobiledevice-glue-93bb30c682bdc984b9acced58e9a1268eefade15.tar.bz2 | |
Add helper for handling colored terminal output
| -rw-r--r-- | include/Makefile.am | 3 | ||||
| -rw-r--r-- | include/libimobiledevice-glue/termcolors.h | 86 | ||||
| -rw-r--r-- | src/Makefile.am | 10 | ||||
| -rw-r--r-- | src/glue.c | 72 | ||||
| -rw-r--r-- | src/termcolors.c | 319 | 
5 files changed, 485 insertions, 5 deletions
| diff --git a/include/Makefile.am b/include/Makefile.am index 5d54892..a661eeb 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -2,4 +2,5 @@ nobase_include_HEADERS = \  	libimobiledevice-glue/socket.h \  	libimobiledevice-glue/thread.h \  	libimobiledevice-glue/utils.h \ -	libimobiledevice-glue/collection.h +	libimobiledevice-glue/collection.h \ +	libimobiledevice-glue/termcolors.h diff --git a/include/libimobiledevice-glue/termcolors.h b/include/libimobiledevice-glue/termcolors.h new file mode 100644 index 0000000..2bac741 --- /dev/null +++ b/include/libimobiledevice-glue/termcolors.h @@ -0,0 +1,86 @@ +/* + * termcolors.h + * + * Copyright (c) 2020-2021 Nikias Bassen, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + */ +#ifndef TERMCOLORS_H +#define TERMCOLORS_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdarg.h> + +#define COLOR_RESET           "\e[m" +#define STYLE_NORMAL          "\e[0m" +#define STYLE_BRIGHT          "\e[1m" +#define STYLE_DARK            "\e[2m" +#define COLOR_BLACK           "\e[0;30m" +#define COLOR_DARK_GRAY       "\e[1;30m" +#define COLOR_RED             "\e[0;31m" +#define COLOR_BRIGHT_RED      "\e[1;31m" +#define COLOR_DARK_RED        "\e[2;31m" +#define COLOR_GREEN           "\e[0;32m" +#define COLOR_BRIGHT_GREEN    "\e[1;32m" +#define COLOR_DARK_GREEN      "\e[2;32m" +#define COLOR_YELLOW          "\e[0;33m" +#define COLOR_BRIGHT_YELLOW   "\e[1;33m" +#define COLOR_DARK_YELLOW     "\e[2;33m" +#define COLOR_BLUE            "\e[0;34m" +#define COLOR_BRIGHT_BLUE     "\e[1;34m" +#define COLOR_DARK_BLUE       "\e[2;34m" +#define COLOR_MAGENTA         "\e[0;35m" +#define COLOR_BRIGHT_MAGENTA  "\e[1;35m" +#define COLOR_DARK_MAGENTA    "\e[2;35m" +#define COLOR_CYAN            "\e[0;36m" +#define COLOR_BRIGHT_CYAN     "\e[1;36m" +#define COLOR_DARK_CYAN       "\e[2;36m" +#define COLOR_LIGHT_GRAY      "\e[0;37m" +#define COLOR_WHITE           "\e[1;37m" +#define COLOR_GRAY            "\e[2;37m" +#define COLOR_DEFAULT         "\e[39m" +#define BG_BLACK              "\e[40m" +#define BG_GRAY               "\e[100m" +#define BG_RED                "\e[41m" +#define BG_BRIGHT_RED         "\e[101m" +#define BG_GREEN              "\e[42m" +#define BG_BRIGHT_GREEN       "\e[102m" +#define BG_YELLOW             "\e[43m" +#define BG_BRIGHT_YELLOW      "\e[103m" +#define BG_BLUE               "\e[44m" +#define BG_BRIGHT_BLUE        "\e[104m" +#define BG_MAGENTA            "\e[45m" +#define BG_BRIGHT_MAGENTA     "\e[105m" +#define BG_CYAN               "\e[46m" +#define BG_BRIGHT_CYAN        "\e[106m" +#define BG_LIGHT_GRAY         "\e[47m" +#define BG_WHITE              "\e[107m" +#define BG_DEFAULT            "\e[49m" + +/* automatically called by library constructor */ +void term_colors_init(); + +/* enable / disable terminal colors */ +void term_colors_set_enabled(int en); + +/* color-aware *printf variants */ +int cprintf(const char* fmt, ...); +int cfprintf(FILE* stream, const char* fmt, ...); +int cvfprintf(FILE* stream, const char* fmt, va_list vargs); + +#endif diff --git a/src/Makefile.am b/src/Makefile.am index 2856eab..67986cb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,10 +8,12 @@ lib_LTLIBRARIES = libimobiledevice-glue-1.0.la  libimobiledevice_glue_1_0_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBIMOBILEDEVICE_GLUE_SO_VERSION) -no-undefined  libimobiledevice_glue_1_0_la_LIBADD =  libimobiledevice_glue_1_0_la_SOURCES =	\ -	socket.c				\ -	thread.c				\ -	utils.c					\ -	collection.c				\ +	glue.c	\ +	socket.c	\ +	thread.c	\ +	utils.c		\ +	collection.c	\ +	termcolors.c	\  	common.h  if WIN32 diff --git a/src/glue.c b/src/glue.c new file mode 100644 index 0000000..e65ef56 --- /dev/null +++ b/src/glue.c @@ -0,0 +1,72 @@ +/* + * glue.c + * + * Copyright (c) 2021 Nikias Bassen, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef WIN32 +#include <windows.h> +#endif + +#include "common.h" +#include "libimobiledevice-glue/thread.h" + +extern void term_colors_init(); + +static void internal_glue_init(void) +{ +	term_colors_init(); +} + +static void internal_glue_deinit(void) +{ + +} + +static thread_once_t init_once = THREAD_ONCE_INIT; +static thread_once_t deinit_once = THREAD_ONCE_INIT; + +#ifdef WIN32 +BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved) +{ +    switch (dwReason) { +    case DLL_PROCESS_ATTACH: +        thread_once(&init_once, internal_glue_init); +        break; +    case DLL_PROCESS_DETACH: +        thread_once(&deinit_once, internal_glue_deinit); +        break; +    default: +        break; +    } +    return 1; +} +#else +static void __attribute__((constructor)) limd_glue_initialize(void) +{ +    thread_once(&init_once, internal_glue_init); +} + +static void __attribute__((destructor)) limd_glue_deinitialize(void) +{ +    thread_once(&deinit_once, internal_glue_deinit); +} +#endif diff --git a/src/termcolors.c b/src/termcolors.c new file mode 100644 index 0000000..703df30 --- /dev/null +++ b/src/termcolors.c @@ -0,0 +1,319 @@ +/* + * termcolors.c + * + * Copyright (c) 2020-2021 Nikias Bassen, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef WIN32 +#include <windows.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdarg.h> + +#include "common.h" +#include "libimobiledevice-glue/termcolors.h" + +static int use_colors = 0; + +#ifdef WIN32 +static int WIN32_LEGACY_MODE = 1; +static WORD COLOR_RESET_ATTR = 0; +static WORD DEFAULT_FG_ATTR = 0; +static WORD DEFAULT_BG_ATTR = 0; +static HANDLE h_stdout = INVALID_HANDLE_VALUE; +static HANDLE h_stderr = INVALID_HANDLE_VALUE; + +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 +#endif + +#define STYLE_DIM (1 << 16) + +#define FG_COLOR_MASK (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE) +#define BG_COLOR_MASK (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE) +#define FG_COLOR_ATTR_MASK (FG_COLOR_MASK | FOREGROUND_INTENSITY) +#define BG_COLOR_ATTR_MASK (BG_COLOR_MASK | BACKGROUND_INTENSITY) + +static int style_map[9] = { +	/* RESET ALL   */ 0, +	/* BRIGHT      */ FOREGROUND_INTENSITY, +	/* DIM         */ STYLE_DIM, +	/* (n/a)       */ 0, +        /* UNDERLINED  */ 0, // COMMON_LVB_UNDERSCORE ? +	/* BLINK       */ 0, // NOT SUPPORTED +	/* (n/a)       */ 0, +	/* REVERSE CLR */ 0, // COMMON_LVB_REVERSE_VIDEO ? +	/* HIDDEN      */ 0  // NOT SUPPORTED +}; + +static int fgcolor_map[8] = { +	/* BLACK   */ 0, +	/* RED     */ FOREGROUND_RED, +	/* GREEN   */ FOREGROUND_GREEN, +	/* YELLOW  */ FOREGROUND_GREEN | FOREGROUND_RED, +	/* BLUE    */ FOREGROUND_BLUE, +	/* MAGENTA */ FOREGROUND_BLUE | FOREGROUND_RED, +	/* CYAN    */ FOREGROUND_BLUE | FOREGROUND_GREEN, +	/* WHITE   */ FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE +}; +static int bgcolor_map[8] = { +	/* BLACK   */ 0, +	/* RED     */ BACKGROUND_RED, +	/* GREEN   */ BACKGROUND_GREEN, +	/* YELLOW  */ BACKGROUND_GREEN | BACKGROUND_RED, +	/* BLUE    */ BACKGROUND_BLUE, +	/* MAGENTA */ BACKGROUND_BLUE | BACKGROUND_RED, +	/* CYAN    */ BACKGROUND_BLUE | BACKGROUND_GREEN, +	/* WHITE   */ BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE +}; +#else +static int WIN32_LEGACY_MODE = 0; +#endif + +LIBIMOBILEDEVICE_GLUE_API void term_colors_init() +{	 +#ifdef WIN32 +	DWORD conmode = 0; +	h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); +	GetConsoleMode(h_stdout, &conmode); +	if (conmode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) { +		WIN32_LEGACY_MODE = 0; +	} else { +		conmode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; +		if (SetConsoleMode(h_stdout, conmode)) { +			WIN32_LEGACY_MODE = 0; +		} else { +			WIN32_LEGACY_MODE = 1; +		} +	} +	if (WIN32_LEGACY_MODE) { +		CONSOLE_SCREEN_BUFFER_INFO csbi; +		if (GetConsoleScreenBufferInfo(h_stdout, &csbi)) { +			COLOR_RESET_ATTR = csbi.wAttributes; +			DEFAULT_FG_ATTR = csbi.wAttributes & FG_COLOR_ATTR_MASK; +			DEFAULT_BG_ATTR = csbi.wAttributes & BG_COLOR_ATTR_MASK; +		} +		h_stderr = GetStdHandle(STD_ERROR_HANDLE); +	} +#endif +	use_colors = isatty(1); +	const char* color_env = getenv("COLOR"); +	if (color_env) { +		long val = strtol(color_env, NULL, 10); +		use_colors = (val != 0); +	} +} + +LIBIMOBILEDEVICE_GLUE_API void term_colors_set_enabled(int en) +{ +	use_colors = en; +} + +LIBIMOBILEDEVICE_GLUE_API int cvfprintf(FILE* stream, const char* fmt, va_list vargs) +{ +	int res = 0; +	int colorize = use_colors; +#ifdef WIN32 +	struct esc_item { +		int pos; +		WORD attr; +	}; +	HANDLE h_stream = h_stdout; + +	if (WIN32_LEGACY_MODE) { +		if (stream == stdout) { +			h_stream = h_stdout; +		} else if (stream == stderr) { +			h_stream = h_stderr; +		} else { +			colorize = 0; +		} +	} +#endif +	if (!colorize || WIN32_LEGACY_MODE) { +		// first, we need to print the string WITH escape sequences +		va_list vargs_copy; +		va_copy(vargs_copy, vargs); +		int len = vsnprintf(NULL, 0, fmt, vargs); +		char* newbuf = (char*)malloc(len+1); +		res = vsnprintf(newbuf, len+1, fmt, vargs_copy); +		va_end(vargs_copy); + +		// then, we need to remove the escape sequences, if any +#ifdef WIN32 +		// if colorize is on, we need to keep their positions for later +		struct esc_item* esc_items = NULL; +		int attr = 0; +		if (colorize) { +			esc_items = (struct esc_item*)malloc(sizeof(struct esc_item) * ((len/3)+1)); +			CONSOLE_SCREEN_BUFFER_INFO csbi; +			if (GetConsoleScreenBufferInfo(h_stream, &csbi)) { +				attr = csbi.wAttributes; +			} +		} +#endif +		int num_esc = 0; +		char* start = &newbuf[0]; +		char* p = start; +		char* end = start + len + 1; +		while (p < end-1) { +			char* cur = p; +			if (*p == '\e' && end-p > 2 && *(p+1) == '[') { +				p+=2; +				if (*p == 'm') { +#ifdef WIN32 +					attr = COLOR_RESET_ATTR; +#endif +					int move_by = (p+1)-cur; +					int move_amount = end-(p+1); +					memmove(cur, p+1, move_amount); +					end -= move_by; +					p = cur; +				} else { +					char* endp = NULL; +					do { +						long lval = strtol(p, &endp, 10); +						if (!endp || (*endp != ';' && *endp != 'm')) { +							fprintf(stderr, "\n*** %s: Invalid escape sequence in format string, expected ';' or 'm' ***\n", __func__); +#ifdef WIN32 +							free(esc_items); +#endif +							free(newbuf); +							return -1; +						} +#ifdef WIN32 +						if (colorize) { +							if (lval >= 0 && lval <= 8) { +								/* style attributes */ +								attr &= ~FOREGROUND_INTENSITY; +								attr |= style_map[lval]; +							} else if (lval >= 30 && lval <= 37) { +								/* foreground color */ +								attr &= ~FG_COLOR_MASK; +								attr |= fgcolor_map[lval-30]; +							} else if (lval == 39) { +								/* default foreground color */ +								attr &= ~FG_COLOR_ATTR_MASK; +								attr |= DEFAULT_FG_ATTR; +							} else if (lval >= 40 && lval <= 47) { +								/* background color */ +								attr &= ~BG_COLOR_MASK; +								attr |= bgcolor_map[lval-40]; +							} else if (lval == 49) { +								/* default background color */ +								attr &= ~BG_COLOR_ATTR_MASK; +								attr |= DEFAULT_BG_ATTR; +							} else if (lval >= 90 && lval <= 97) { +								/* foreground color bright */ +								attr &= ~FG_COLOR_ATTR_MASK; +								attr |= fgcolor_map[lval-90]; +								attr |= FOREGROUND_INTENSITY; +							} else if (lval >= 100 && lval <= 107) { +								/* background color bright */ +								attr &= ~BG_COLOR_MASK; +								attr |= bgcolor_map[lval-100]; +								attr |= BACKGROUND_INTENSITY; +							} +						} +#else +						(void)lval; // suppress compiler warning about unused variable +#endif +						p = endp+1; +					} while (p < end && *endp == ';'); + +					int move_by = (endp+1)-cur; +					int move_amount = end-(endp+1); +					memmove(cur, endp+1, move_amount); +					end -= move_by; +					p = cur; +				} +#ifdef WIN32 +				if (colorize) { +					esc_items[num_esc].pos = p-start; +					if (attr & STYLE_DIM) { +						attr &= ~STYLE_DIM; +						attr &= ~FOREGROUND_INTENSITY; +					} +					esc_items[num_esc].attr = (WORD)attr; +					num_esc++; +				} +#endif +			} else { +				p++; +			} +		} + +		if (num_esc == 0) { +			res = fputs(newbuf, stream); +			free(newbuf); +#ifdef WIN32 +			free(esc_items); +#endif +			return res; +		} +#ifdef WIN32 +		else { +			p = &newbuf[0]; +			char* lastp = &newbuf[0]; +			int i; +			for (i = 0; i < num_esc; i++) { +				p = &newbuf[esc_items[i].pos]; +				if (lastp < p) { +					fprintf(stream, "%.*s", p-lastp, lastp); +					lastp = p; +				} +				SetConsoleTextAttribute(h_stream, esc_items[i].attr); +			} +			if (lastp < end) { +				fprintf(stream, "%.*s", end-lastp, lastp); +			} +			return res; +		} +#endif +	} else { +		res = vfprintf(stream, fmt, vargs); +	} +	return res; +} + +LIBIMOBILEDEVICE_GLUE_API int cfprintf(FILE* stream, const char* fmt, ...) +{ +	int res = 0; +	va_list va; +	va_start(va, fmt); +	res = cvfprintf(stream, fmt, va); +	va_end(va); +	return res; +} + +LIBIMOBILEDEVICE_GLUE_API int cprintf(const char* fmt, ...) +{ +	int res = 0; +	va_list va; +	va_start(va, fmt); +	res = cvfprintf(stdout, fmt, va); +	va_end(va); +	return res; +} | 
