132b7fa0cce9d39541e01a1787163029b09c9d66
[asterisk/asterisk.git] / main / term.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2010, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Terminal Routines
22  *
23  * \author Mark Spencer <markster@digium.com>
24  */
25
26 /*** MODULEINFO
27         <support_level>core</support_level>
28  ***/
29
30 #include "asterisk.h"
31
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
33
34 #include "asterisk/_private.h"
35 #include <sys/time.h>
36 #include <signal.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39
40 #include "asterisk/term.h"
41 #include "asterisk/lock.h"
42 #include "asterisk/utils.h"
43
44 static int vt100compat;
45
46 static char prepdata[80] = "";
47 static char enddata[80] = "";
48 static char quitdata[80] = "";
49
50 static const char * const termpath[] = {
51         "/usr/share/terminfo",
52         "/usr/local/share/misc/terminfo",
53         "/usr/lib/terminfo",
54         NULL
55         };
56
57 static int opposite(int color)
58 {
59         int lookup[] = {
60                 /* BLACK */ COLOR_BLACK,
61                 /* RED */ COLOR_MAGENTA,
62                 /* GREEN */ COLOR_GREEN,
63                 /* BROWN */ COLOR_BROWN,
64                 /* BLUE */ COLOR_CYAN,
65                 /* MAGENTA */ COLOR_RED,
66                 /* CYAN */ COLOR_BLUE,
67                 /* WHITE */ COLOR_BLACK };
68         return color ? lookup[color - 30] : 0;
69 }
70
71 /* Ripped off from Ross Ridge, but it's public domain code (libmytinfo) */
72 static short convshort(char *s)
73 {
74         register int a, b;
75
76         a = (int) s[0] & 0377;
77         b = (int) s[1] & 0377;
78
79         if (a == 0377 && b == 0377)
80                 return -1;
81         if (a == 0376 && b == 0377)
82                 return -2;
83
84         return a + b * 256;
85 }
86
87 int ast_term_init(void)
88 {
89         char *term = getenv("TERM");
90         char termfile[256] = "";
91         char buffer[512] = "";
92         int termfd = -1, parseokay = 0, i;
93
94         if (ast_opt_no_color) {
95                 return 0;
96         }
97
98         if (!ast_opt_console) {
99                 /* If any remote console is not compatible, we'll strip the color codes at that point */
100                 vt100compat = 1;
101                 goto end;
102         }
103
104         if (!term) {
105                 return 0;
106         }
107
108         for (i = 0;; i++) {
109                 if (termpath[i] == NULL) {
110                         break;
111                 }
112                 snprintf(termfile, sizeof(termfile), "%s/%c/%s", termpath[i], *term, term);
113                 termfd = open(termfile, O_RDONLY);
114                 if (termfd > -1) {
115                         break;
116                 }
117         }
118         if (termfd > -1) {
119                 int actsize = read(termfd, buffer, sizeof(buffer) - 1);
120                 short sz_names = convshort(buffer + 2);
121                 short sz_bools = convshort(buffer + 4);
122                 short n_nums   = convshort(buffer + 6);
123
124                 /* if ((sz_names + sz_bools) & 1)
125                         sz_bools++; */
126
127                 if (sz_names + sz_bools + n_nums < actsize) {
128                         /* Offset 13 is defined in /usr/include/term.h, though we do not
129                          * include it here, as it conflicts with include/asterisk/term.h */
130                         short max_colors = convshort(buffer + 12 + sz_names + sz_bools + 13 * 2);
131                         if (max_colors > 0) {
132                                 vt100compat = 1;
133                         }
134                         parseokay = 1;
135                 }
136                 close(termfd);
137         }
138
139         if (!parseokay) {
140                 /* These comparisons should not be substrings nor case-insensitive, as
141                  * terminal types are very particular about how they treat suffixes and
142                  * capitalization.  For example, terminal type 'linux-m' does NOT
143                  * support color, while 'linux' does.  Not even all vt100* terminals
144                  * support color, either (e.g. 'vt100+fnkeys'). */
145                 if (!strcmp(term, "linux")) {
146                         vt100compat = 1;
147                 } else if (!strcmp(term, "xterm")) {
148                         vt100compat = 1;
149                 } else if (!strcmp(term, "xterm-color")) {
150                         vt100compat = 1;
151                 } else if (!strcmp(term, "xterm-256color")) {
152                         vt100compat = 1;
153                 } else if (!strncmp(term, "Eterm", 5)) {
154                         /* Both entries which start with Eterm support color */
155                         vt100compat = 1;
156                 } else if (!strcmp(term, "vt100")) {
157                         vt100compat = 1;
158                 } else if (!strncmp(term, "crt", 3)) {
159                         /* Both crt terminals support color */
160                         vt100compat = 1;
161                 }
162         }
163
164 end:
165         if (vt100compat) {
166                 /* Make commands show up in nice colors */
167                 if (ast_opt_light_background) {
168                         snprintf(prepdata, sizeof(prepdata), "%c[%dm", ESC, COLOR_BROWN);
169                         snprintf(enddata, sizeof(enddata), "%c[%dm", ESC, COLOR_BLACK);
170                         snprintf(quitdata, sizeof(quitdata), "%c[0m", ESC);
171                 } else if (ast_opt_force_black_background) {
172                         snprintf(prepdata, sizeof(prepdata), "%c[%d;%d;%dm", ESC, ATTR_BRIGHT, COLOR_BROWN, COLOR_BLACK + 10);
173                         snprintf(enddata, sizeof(enddata), "%c[%d;%d;%dm", ESC, ATTR_RESET, COLOR_WHITE, COLOR_BLACK + 10);
174                         snprintf(quitdata, sizeof(quitdata), "%c[0m", ESC);
175                 } else {
176                         snprintf(prepdata, sizeof(prepdata), "%c[%d;%dm", ESC, ATTR_BRIGHT, COLOR_BROWN);
177                         snprintf(enddata, sizeof(enddata), "%c[%d;%dm", ESC, ATTR_RESET, COLOR_WHITE);
178                         snprintf(quitdata, sizeof(quitdata), "%c[0m", ESC);
179                 }
180         }
181         return 0;
182 }
183
184 char *term_color(char *outbuf, const char *inbuf, int fgcolor, int bgcolor, int maxout)
185 {
186         int attr = 0;
187
188         if (!vt100compat) {
189                 ast_copy_string(outbuf, inbuf, maxout);
190                 return outbuf;
191         }
192         if (!fgcolor) {
193                 ast_copy_string(outbuf, inbuf, maxout);
194                 return outbuf;
195         }
196
197         if (fgcolor & 128) {
198                 attr = ast_opt_light_background ? 0 : ATTR_BRIGHT;
199                 fgcolor &= ~128;
200         }
201
202         if (bgcolor) {
203                 bgcolor &= ~128;
204         }
205
206         if (ast_opt_light_background) {
207                 fgcolor = opposite(fgcolor);
208         }
209
210         if (ast_opt_force_black_background) {
211                 snprintf(outbuf, maxout, "%c[%d;%d;%dm%s%c[%d;%dm", ESC, attr, fgcolor, bgcolor + 10, inbuf, ESC, COLOR_WHITE, COLOR_BLACK + 10);
212         } else {
213                 snprintf(outbuf, maxout, "%c[%d;%dm%s%c[0m", ESC, attr, fgcolor, inbuf, ESC);
214         }
215         return outbuf;
216 }
217
218 static void check_fgcolor(int *fgcolor, int *attr)
219 {
220         if (*fgcolor & 128) {
221                 *attr = ast_opt_light_background ? 0 : ATTR_BRIGHT;
222                 *fgcolor &= ~128;
223         }
224         
225         if (ast_opt_light_background) {
226                 *fgcolor = opposite(*fgcolor);
227         }
228 }
229
230 static void check_bgcolor(int *bgcolor)
231 {
232         if (*bgcolor) {
233                 *bgcolor &= ~128;
234         }
235 }
236
237 static int check_colors_allowed(int fgcolor)
238 {
239         return (!vt100compat || !fgcolor) ? 0 : 1;
240 }
241
242 int ast_term_color_code(struct ast_str **str, int fgcolor, int bgcolor)
243 {
244         int attr = 0;
245
246         if (!check_colors_allowed(fgcolor)) {
247                 return -1;
248         }
249
250         check_fgcolor(&fgcolor, &attr);
251         check_bgcolor(&bgcolor);
252         
253         if (ast_opt_force_black_background) {
254                 ast_str_append(str, 0, "%c[%d;%d;%dm", ESC, attr, fgcolor, COLOR_BLACK + 10);
255         } else if (bgcolor) {
256                 ast_str_append(str, 0, "%c[%d;%d;%dm", ESC, attr, fgcolor, bgcolor + 10);
257         } else {
258                 ast_str_append(str, 0, "%c[%d;%dm", ESC, attr, fgcolor);
259         }
260
261         return 0;
262 }
263
264 char *term_color_code(char *outbuf, int fgcolor, int bgcolor, int maxout)
265 {
266         int attr = 0;
267
268         if (!check_colors_allowed(fgcolor)) {
269                 *outbuf = '\0';
270                 return outbuf;
271         }
272
273         check_fgcolor(&fgcolor, &attr);
274         check_bgcolor(&bgcolor);
275
276         if (ast_opt_force_black_background) {
277                 snprintf(outbuf, maxout, "%c[%d;%d;%dm", ESC, attr, fgcolor, COLOR_BLACK + 10);
278         } else if (bgcolor) {
279                 snprintf(outbuf, maxout, "%c[%d;%d;%dm", ESC, attr, fgcolor, bgcolor + 10);
280         } else {
281                 snprintf(outbuf, maxout, "%c[%d;%dm", ESC, attr, fgcolor);
282         }
283
284         return outbuf;
285 }
286
287 char *term_strip(char *outbuf, const char *inbuf, int maxout)
288 {
289         char *outbuf_ptr = outbuf;
290         const char *inbuf_ptr = inbuf;
291
292         while (outbuf_ptr < outbuf + maxout) {
293                 switch (*inbuf_ptr) {
294                         case ESC:
295                                 while (*inbuf_ptr && (*inbuf_ptr != 'm'))
296                                         inbuf_ptr++;
297                                 break;
298                         default:
299                                 *outbuf_ptr = *inbuf_ptr;
300                                 outbuf_ptr++;
301                 }
302                 if (! *inbuf_ptr)
303                         break;
304                 inbuf_ptr++;
305         }
306         return outbuf;
307 }
308
309 char *term_prompt(char *outbuf, const char *inbuf, int maxout)
310 {
311         if (!vt100compat) {
312                 ast_copy_string(outbuf, inbuf, maxout);
313                 return outbuf;
314         }
315         if (ast_opt_force_black_background) {
316                 snprintf(outbuf, maxout, "%c[%d;%dm%c%c[%d;%dm%s",
317                         ESC, COLOR_BLUE, COLOR_BLACK + 10,
318                         inbuf[0],
319                         ESC, COLOR_WHITE, COLOR_BLACK + 10,
320                         inbuf + 1);
321         } else if (ast_opt_light_background) {
322                 snprintf(outbuf, maxout, "%c[%d;0m%c%c[%d;0m%s",
323                         ESC, COLOR_BLUE,
324                         inbuf[0],
325                         ESC, COLOR_BLACK,
326                         inbuf + 1);
327         } else {
328                 snprintf(outbuf, maxout, "%c[%d;%d;0m%c%c[%d;%d;0m%s",
329                         ESC, ATTR_BRIGHT, COLOR_BLUE,
330                         inbuf[0],
331                         ESC, 0, COLOR_WHITE,
332                         inbuf + 1);
333         }
334         return outbuf;
335 }
336
337 /* filter escape sequences */
338 void term_filter_escapes(char *line)
339 {
340         int i;
341         int len = strlen(line);
342
343         for (i = 0; i < len; i++) {
344                 if (line[i] != ESC)
345                         continue;
346                 if ((i < (len - 2)) &&
347                     (line[i + 1] == 0x5B)) {
348                         switch (line[i + 2]) {
349                         case 0x30:
350                         case 0x31:
351                         case 0x33:
352                                 continue;
353                         }
354                 }
355                 /* replace ESC with a space */
356                 line[i] = ' ';
357         }
358 }
359
360 char *term_prep(void)
361 {
362         return prepdata;
363 }
364
365 char *term_end(void)
366 {
367         return enddata;
368 }
369
370 char *term_quit(void)
371 {
372         return quitdata;
373 }