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