Asterisk - The Open Source Telephony Project  GIT-master-4a4f1a5
term.c
Go to the documentation of this file.
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 #include "asterisk/_private.h"
33 #include <sys/time.h>
34 #include <signal.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 
38 #include "asterisk/term.h"
39 #include "asterisk/lock.h"
40 #include "asterisk/utils.h"
41 #include "asterisk/threadstorage.h"
42 
43 static int vt100compat;
44 
45 static char enddata[80] = "";
46 static char quitdata[80] = "";
47 
48 static const char * const termpath[] = {
49  "/usr/share/terminfo",
50  "/usr/local/share/misc/terminfo",
51  "/usr/lib/terminfo",
52  NULL
53  };
54 
56 
57 struct commonbuf {
58  short which;
60 };
61 
62 static int opposite(int color)
63 {
64  int lookup[] = {
65  /* BLACK */ COLOR_BLACK,
66  /* RED */ COLOR_MAGENTA,
67  /* GREEN */ COLOR_GREEN,
68  /* BROWN */ COLOR_BROWN,
69  /* BLUE */ COLOR_CYAN,
70  /* MAGENTA */ COLOR_RED,
71  /* CYAN */ COLOR_BLUE,
72  /* WHITE */ COLOR_BLACK };
73  return color ? lookup[color - 30] : 0;
74 }
75 
76 /* Ripped off from Ross Ridge, but it's public domain code (libmytinfo) */
77 static short convshort(unsigned char *s)
78 {
79  register int a, b;
80 
81  a = (int) s[0] & 0377;
82  b = (int) s[1] & 0377;
83 
84  if (a == 0377 && b == 0377)
85  return -1;
86  if (a == 0376 && b == 0377)
87  return -2;
88 
89  return a + b * 256;
90 }
91 
92 static inline int convint(unsigned char *s)
93 {
94  return s[0]
95  | s[1] << 8
96  | s[2] << 16
97  | s[3] << 24;
98 }
99 
100 #define MAGIC_LEGACY (00432)
101 #define MAGIC_EXTNUM (01036)
102 
103 #define HEADER_LEN (12)
104 #define MAX_COLORS_INDEX (13)
105 
106 static int parse_terminfo_file(int fd)
107 {
108  int bytes_read, bytes_needed, num_size;
109  short magic, sz_names, sz_bools;
110  unsigned char buffer[1024];
111 
112  bytes_read = read(fd, buffer, sizeof(buffer));
113  if (bytes_read < HEADER_LEN) {
114  return 0;
115  }
116 
117  magic = convshort(buffer);
118 
119  if (magic == MAGIC_LEGACY) {
120  num_size = 2;
121  } else if (magic == MAGIC_EXTNUM) {
122  /* Extended number format (ncurses 6.1) */
123  num_size = 4;
124  } else {
125  /* We don't know how to parse this file */
126  return 0;
127  }
128 
129  sz_names = convshort(buffer + 2);
130  sz_bools = convshort(buffer + 4);
131 
132  /* From term(5):
133  * Between the boolean section and the number section, a null byte will be
134  * inserted, if necessary, to ensure that the number section begins on an
135  * even byte. */
136  if ((sz_names + sz_bools) & 1) {
137  sz_bools++;
138  }
139 
140  bytes_needed = HEADER_LEN + sz_names + sz_bools + ((MAX_COLORS_INDEX + 1) * num_size);
141  if (bytes_needed <= bytes_read) {
142  /* Offset 13 is defined in /usr/include/term.h, though we do not
143  * include it here, as it conflicts with include/asterisk/term.h */
144  int max_colors;
145  int offset = HEADER_LEN + sz_names + sz_bools + MAX_COLORS_INDEX * num_size;
146 
147  if (num_size == 2) {
148  /* In the legacy terminfo format, numbers are signed shorts */
149  max_colors = convshort(buffer + offset);
150  } else {
151  /* Extended number format makes them signed ints */
152  max_colors = convint(buffer + offset);
153  }
154 
155  if (max_colors > 0) {
156  vt100compat = 1;
157  }
158 
159  return 1;
160  }
161 
162  return 0;
163 }
164 
165 int ast_term_init(void)
166 {
167  char *term = getenv("TERM");
168  char termfile[256] = "";
169  int termfd = -1, parseokay = 0, i;
170 
171  if (ast_opt_no_color) {
172  return 0;
173  }
174 
175  if (!ast_opt_console) {
176  /* If any remote console is not compatible, we'll strip the color codes at that point */
177  vt100compat = 1;
178  goto end;
179  }
180 
181  if (!term) {
182  return 0;
183  }
184 
185  for (i = 0; !parseokay && termpath[i]; i++) {
186  snprintf(termfile, sizeof(termfile), "%s/%c/%s", termpath[i], *term, term);
187 
188  termfd = open(termfile, O_RDONLY);
189  if (termfd > -1) {
190  parseokay = parse_terminfo_file(termfd);
191  close(termfd);
192  }
193  }
194 
195  if (!parseokay) {
196  /* These comparisons should not be substrings nor case-insensitive, as
197  * terminal types are very particular about how they treat suffixes and
198  * capitalization. For example, terminal type 'linux-m' does NOT
199  * support color, while 'linux' does. Not even all vt100* terminals
200  * support color, either (e.g. 'vt100+fnkeys'). */
201  if (!strcmp(term, "linux")) {
202  vt100compat = 1;
203  } else if (!strcmp(term, "xterm")) {
204  vt100compat = 1;
205  } else if (!strcmp(term, "xterm-color")) {
206  vt100compat = 1;
207  } else if (!strcmp(term, "xterm-256color")) {
208  vt100compat = 1;
209  } else if (!strncmp(term, "Eterm", 5)) {
210  /* Both entries which start with Eterm support color */
211  vt100compat = 1;
212  } else if (!strcmp(term, "vt100")) {
213  vt100compat = 1;
214  } else if (!strncmp(term, "crt", 3)) {
215  /* Both crt terminals support color */
216  vt100compat = 1;
217  }
218  }
219 
220 end:
221  if (vt100compat) {
222  /* Make commands show up in nice colors */
224  snprintf(enddata, sizeof(enddata), "%c[%dm", ESC, COLOR_BLACK);
225  } else if (ast_opt_force_black_background) {
226  snprintf(enddata, sizeof(enddata), "%c[%d;%d;%dm", ESC, ATTR_RESET, COLOR_WHITE, COLOR_BLACK + 10);
227  } else {
228  snprintf(enddata, sizeof(enddata), "%c[%dm", ESC, ATTR_RESET);
229  }
230  snprintf(quitdata, sizeof(quitdata), "%c[%dm", ESC, ATTR_RESET);
231  }
232  return 0;
233 }
234 
235 char *term_color(char *outbuf, const char *inbuf, int fgcolor, int bgcolor, int maxout)
236 {
237  int attr = 0;
238 
239  if (!vt100compat) {
240  ast_copy_string(outbuf, inbuf, maxout);
241  return outbuf;
242  }
243  if (!fgcolor) {
244  ast_copy_string(outbuf, inbuf, maxout);
245  return outbuf;
246  }
247 
248  if (fgcolor & 128) {
250  fgcolor &= ~128;
251  }
252 
253  if (bgcolor) {
254  bgcolor &= ~128;
255  }
256 
258  fgcolor = opposite(fgcolor);
259  }
260 
262  if (!bgcolor) {
263  bgcolor = COLOR_BLACK;
264  }
265  snprintf(outbuf, maxout, "%c[%d;%d;%dm%s%s", ESC, attr, fgcolor, bgcolor + 10, inbuf, term_end());
266  } else {
267  snprintf(outbuf, maxout, "%c[%d;%dm%s%s", ESC, attr, fgcolor, inbuf, term_end());
268  }
269  return outbuf;
270 }
271 
272 static void check_fgcolor(int *fgcolor, int *attr)
273 {
275  if (*fgcolor & 128) {
276  *fgcolor &= ~128;
277  }
278 
280  *fgcolor = opposite(*fgcolor);
281  }
282 }
283 
284 static void check_bgcolor(int *bgcolor)
285 {
286  if (*bgcolor) {
287  *bgcolor &= ~128;
288  }
289 }
290 
291 static int check_colors_allowed(void)
292 {
293  return vt100compat;
294 }
295 
296 int ast_term_color_code(struct ast_str **str, int fgcolor, int bgcolor)
297 {
298  int attr = 0;
299 
300  if (!check_colors_allowed()) {
301  return -1;
302  }
303 
304  check_fgcolor(&fgcolor, &attr);
305  check_bgcolor(&bgcolor);
306 
308  ast_str_append(str, 0, "%c[%d;%d;%dm", ESC, attr, fgcolor, COLOR_BLACK + 10);
309  } else if (bgcolor) {
310  ast_str_append(str, 0, "%c[%d;%d;%dm", ESC, attr, fgcolor, bgcolor + 10);
311  } else {
312  ast_str_append(str, 0, "%c[%d;%dm", ESC, attr, fgcolor);
313  }
314 
315  return 0;
316 }
317 
318 char *term_color_code(char *outbuf, int fgcolor, int bgcolor, int maxout)
319 {
320  int attr = 0;
321 
322  if (!check_colors_allowed()) {
323  *outbuf = '\0';
324  return outbuf;
325  }
326 
327  check_fgcolor(&fgcolor, &attr);
328  check_bgcolor(&bgcolor);
329 
331  snprintf(outbuf, maxout, "%c[%d;%d;%dm", ESC, attr, fgcolor, COLOR_BLACK + 10);
332  } else if (bgcolor) {
333  snprintf(outbuf, maxout, "%c[%d;%d;%dm", ESC, attr, fgcolor, bgcolor + 10);
334  } else {
335  snprintf(outbuf, maxout, "%c[%d;%dm", ESC, attr, fgcolor);
336  }
337 
338  return outbuf;
339 }
340 
341 const char *ast_term_color(int fgcolor, int bgcolor)
342 {
343  struct commonbuf *cb = ast_threadstorage_get(&commonbuf, sizeof(*cb));
344  char *buf;
345 
346  if (!cb) {
347  return "";
348  }
349  buf = cb->buffer[cb->which++];
351  cb->which = 0;
352  }
353 
354  return term_color_code(buf, fgcolor, bgcolor, AST_TERM_MAX_ESCAPE_CHARS);
355 }
356 
357 const char *ast_term_reset(void)
358 {
359  return term_end();
360 }
361 
362 char *term_strip(char *outbuf, const char *inbuf, int maxout)
363 {
364  char *outbuf_ptr = outbuf;
365  const char *inbuf_ptr = inbuf;
366 
367  while (outbuf_ptr < outbuf + maxout) {
368  switch (*inbuf_ptr) {
369  case ESC:
370  while (*inbuf_ptr && (*inbuf_ptr != 'm'))
371  inbuf_ptr++;
372  break;
373  default:
374  *outbuf_ptr = *inbuf_ptr;
375  outbuf_ptr++;
376  }
377  if (! *inbuf_ptr)
378  break;
379  inbuf_ptr++;
380  }
381  return outbuf;
382 }
383 
384 /* filter escape sequences */
385 void term_filter_escapes(char *line)
386 {
387  int i;
388  int len = strlen(line);
389 
390  for (i = 0; i < len; i++) {
391  if (line[i] != ESC)
392  continue;
393  if ((i < (len - 2)) &&
394  (line[i + 1] == 0x5B)) {
395  switch (line[i + 2]) {
396  case 0x30:
397  case 0x31:
398  case 0x33:
399  continue;
400  }
401  }
402  /* replace ESC with a space */
403  line[i] = ' ';
404  }
405 }
406 
407 const char *term_end(void)
408 {
409  return enddata;
410 }
411 
412 const char *term_quit(void)
413 {
414  return quitdata;
415 }
Prototypes for public functions only of internal interest,.
const char * str
Definition: app_jack.c:147
static int inbuf(struct baseio *bio, FILE *fi)
utility used by inchar(), for base_encode()
Asterisk main include file. File version handling, generic pbx functions.
char * end
Definition: eagi_proxy.c:73
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
Asterisk locking-related definitions:
#define ast_opt_console
Definition: options.h:109
#define ast_opt_no_color
Definition: options.h:114
#define ast_opt_force_black_background
Definition: options.h:131
#define ast_opt_light_background
Definition: options.h:130
#define NULL
Definition: resample.c:96
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1104
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:597
Definition: term.c:57
short which
Definition: term.c:58
char buffer[AST_TERM_MAX_ROTATING_BUFFERS][AST_TERM_MAX_ESCAPE_CHARS]
Definition: term.c:59
#define MAGIC_LEGACY
Definition: term.c:100
static int check_colors_allowed(void)
Definition: term.c:291
const char * ast_term_reset(void)
Returns the terminal reset code.
Definition: term.c:357
static int vt100compat
Definition: term.c:43
int ast_term_init(void)
Definition: term.c:165
const char * term_end(void)
Definition: term.c:407
char * term_strip(char *outbuf, const char *inbuf, int maxout)
Remove colorings from a specified string.
Definition: term.c:362
static short convshort(unsigned char *s)
Definition: term.c:77
int ast_term_color_code(struct ast_str **str, int fgcolor, int bgcolor)
Append a color sequence to an ast_str.
Definition: term.c:296
char * term_color(char *outbuf, const char *inbuf, int fgcolor, int bgcolor, int maxout)
Colorize a specified string by adding terminal color codes.
Definition: term.c:235
static int convint(unsigned char *s)
Definition: term.c:92
static char enddata[80]
Definition: term.c:45
static int parse_terminfo_file(int fd)
Definition: term.c:106
static char quitdata[80]
Definition: term.c:46
const char * ast_term_color(int fgcolor, int bgcolor)
Return a color sequence string.
Definition: term.c:341
static void check_bgcolor(int *bgcolor)
Definition: term.c:284
#define HEADER_LEN
Definition: term.c:103
void term_filter_escapes(char *line)
Definition: term.c:385
#define MAGIC_EXTNUM
Definition: term.c:101
static void check_fgcolor(int *fgcolor, int *attr)
Definition: term.c:272
static const char *const termpath[]
Definition: term.c:48
#define MAX_COLORS_INDEX
Definition: term.c:104
char * term_color_code(char *outbuf, int fgcolor, int bgcolor, int maxout)
Write a color sequence to a string.
Definition: term.c:318
static int opposite(int color)
Definition: term.c:62
const char * term_quit(void)
Definition: term.c:412
Handy terminal functions for vt* terms.
#define ATTR_BRIGHT
Definition: term.h:36
#define COLOR_BLUE
Definition: term.h:55
#define ESC
Definition: term.h:30
#define AST_TERM_MAX_ROTATING_BUFFERS
Definition: term.h:72
#define COLOR_CYAN
Definition: term.h:59
#define COLOR_MAGENTA
Definition: term.h:57
#define COLOR_BROWN
Definition: term.h:53
#define COLOR_WHITE
Definition: term.h:61
#define COLOR_BLACK
Definition: term.h:47
#define ATTR_RESET
Definition: term.h:35
#define AST_TERM_MAX_ESCAPE_CHARS
Maximum number of characters needed for a color escape sequence, and another one for a trailing reset...
Definition: term.h:71
#define COLOR_RED
Definition: term.h:49
#define COLOR_GREEN
Definition: term.h:51
static struct test_val b
static struct test_val a
Definitions to aid in the use of thread local storage.
void * ast_threadstorage_get(struct ast_threadstorage *ts, size_t init_size)
Retrieve thread storage.
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:86
Utility functions.