summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2021-06-11 01:37:27 +0200
committerGravatar Nikias Bassen2021-06-11 01:37:27 +0200
commit93bb30c682bdc984b9acced58e9a1268eefade15 (patch)
treedff9e6157875b5b3e63015bd6cb6f8e9873ed1db
parent0dcfb68954f7ee9957f528f0eea1f9dba9c9cb68 (diff)
downloadlibimobiledevice-glue-93bb30c682bdc984b9acced58e9a1268eefade15.tar.gz
libimobiledevice-glue-93bb30c682bdc984b9acced58e9a1268eefade15.tar.bz2
Add helper for handling colored terminal output
-rw-r--r--include/Makefile.am3
-rw-r--r--include/libimobiledevice-glue/termcolors.h86
-rw-r--r--src/Makefile.am10
-rw-r--r--src/glue.c72
-rw-r--r--src/termcolors.c319
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;
+}