1 /*
2 $Id: console.c 2522 2021-03-14 20:16:55Z soci $
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
18 */
19 #include "console.h"
20
21 #ifdef COLOR_OUTPUT
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25
26 bool console_use_color = false;
27 bool console_use_bold = false;
28
29 enum terminal_e {
30 TERMINAL_UNKNOWN, TERMINAL_OK, TERMINAL_DUMB
31 };
32
33 static enum terminal_e terminal = TERMINAL_UNKNOWN;
34
terminal_detect(FILE * f)35 static bool terminal_detect(FILE *f) {
36 int fd;
37 if (f == stderr) {
38 fd = STDERR_FILENO;
39 } else if (f == stdout) {
40 fd = STDOUT_FILENO;
41 } else {
42 return false;
43 }
44 if (terminal == TERMINAL_UNKNOWN) {
45 char const *term = getenv("TERM");
46 terminal = (term != NULL && strcmp(term, "dumb") != 0) ? TERMINAL_OK : TERMINAL_DUMB;
47 }
48 return terminal == TERMINAL_OK && isatty(fd) == 1;
49 }
50
51 #ifdef _WIN32
52 #include <windows.h>
53
54 static bool use_ansi;
55 static BOOL utf8_console;
56 static UINT old_consoleoutputcp;
57 static UINT old_consolecp;
58 static HANDLE console_handle;
59 static int old_attributes, current_attributes;
60
console_init(void)61 void console_init(void) {
62 utf8_console = IsValidCodePage(CP_UTF8);
63 if (utf8_console) {
64 old_consoleoutputcp = GetConsoleOutputCP();
65 old_consolecp = GetConsoleCP();
66 SetConsoleCP(CP_UTF8);
67 SetConsoleOutputCP(CP_UTF8);
68 }
69 }
70
console_destroy(void)71 void console_destroy(void) {
72 if (utf8_console) {
73 SetConsoleCP(old_consolecp);
74 SetConsoleOutputCP(old_consoleoutputcp);
75 }
76 }
77
console_use(FILE * f)78 void console_use(FILE *f) {
79 CONSOLE_SCREEN_BUFFER_INFO console_info;
80 DWORD handle;
81
82 use_ansi = terminal_detect(f);
83 if (use_ansi) {
84 console_use_color = true;
85 return;
86 }
87 console_use_color = false;
88 if (f == stderr) {
89 handle = STD_ERROR_HANDLE;
90 } else if (f == stdout) {
91 handle = STD_OUTPUT_HANDLE;
92 } else {
93 return;
94 }
95 console_handle = GetStdHandle(handle);
96 if (console_handle == INVALID_HANDLE_VALUE) {
97 return;
98 }
99 if (GetConsoleScreenBufferInfo(console_handle, &console_info)) {
100 old_attributes = current_attributes = console_info.wAttributes;
101 console_use_color = true;
102 return;
103 }
104 }
105
106 static const char *const ansi_sequences[8] = {
107 "\33[1m", "\33[0;1m", "\33[7m", "\33[m", "\33[36m", "\33[31m", "\33[1;32m",
108 "\33[35m"
109 };
110
console_attribute(int c,FILE * f)111 void console_attribute(int c, FILE *f) {
112 if (use_ansi) {
113 fputs(ansi_sequences[c], f);
114 return;
115 }
116 fflush(f);
117 switch (c) {
118 case 0: current_attributes |= FOREGROUND_INTENSITY; break;
119 case 1: current_attributes = old_attributes | FOREGROUND_INTENSITY; break;
120 case 2:
121 if (!(current_attributes & FOREGROUND_BLUE) != !(current_attributes & BACKGROUND_BLUE)) current_attributes ^= FOREGROUND_BLUE | BACKGROUND_BLUE;
122 if (!(current_attributes & FOREGROUND_GREEN) != !(current_attributes & BACKGROUND_GREEN)) current_attributes ^= FOREGROUND_GREEN | BACKGROUND_GREEN;
123 if (!(current_attributes & FOREGROUND_RED) != !(current_attributes & BACKGROUND_RED)) current_attributes ^= FOREGROUND_RED | BACKGROUND_RED;
124 if (!(current_attributes & FOREGROUND_INTENSITY) != !(current_attributes & BACKGROUND_INTENSITY)) current_attributes ^= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY;
125 break;
126 case 3: current_attributes = old_attributes; break;
127 case 4: current_attributes = FOREGROUND_GREEN | FOREGROUND_BLUE | (current_attributes & ~FOREGROUND_RED); break;
128 case 5: current_attributes = FOREGROUND_RED | (current_attributes & ~(FOREGROUND_BLUE | FOREGROUND_GREEN)); break;
129 case 6: current_attributes = FOREGROUND_GREEN | FOREGROUND_INTENSITY | (current_attributes & ~(FOREGROUND_BLUE | FOREGROUND_RED)); break;
130 case 7: current_attributes = FOREGROUND_RED | FOREGROUND_BLUE | (current_attributes & ~FOREGROUND_GREEN); break;
131 default: break;
132 }
133 SetConsoleTextAttribute(console_handle, (WORD)current_attributes);
134 }
135 #else
console_use(FILE * f)136 void console_use(FILE *f) {
137 console_use_color = terminal_detect(f);
138 }
139 #endif
140
141 #endif
142