1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright (c) 1995-1999 by Sun Microsystems, Inc.
24 * All rights reserved.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 /* LINTLIBRARY */
30
31 /*
32 * setupterm.c
33 *
34 * XCurses Library
35 *
36 * Copyright 1990, 1995 by Mortice Kern Systems Inc. All rights reserved.
37 *
38 */
39
40 #ifdef M_RCSID
41 #ifndef lint
42 static char const rcsID[] =
43 "$Header: /team/ps/sun_xcurses/archive/local_changes/xcurses/src/lib/"
44 "libxcurses/src/libc/xcurses/rcs/setup.c 1.16 1998/06/05 14:35:33 "
45 "cbates Exp $";
46 #endif
47 #endif
48
49 #include <private.h>
50 #include <sys/types.h>
51 #ifdef TIOCGWINSZ
52 #include <sys/ioctl.h>
53 #endif
54 #include <fcntl.h>
55 #include <limits.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <unistd.h>
59
60 TERMINAL *cur_term;
61
62 /*
63 * Any version number should be placed in this file, since setupterm()
64 * must be called in order to initialize Curses or Terminfo.
65 */
66 char __m_curses_version[] = M_CURSES_VERSION;
67
68 /*
69 * True if __m_setupterm() should use either the window settings from
70 * ioctl(), or the environment variables LINES and COLUMNS to override
71 * the terminfo database entries for 'lines' and 'columns'.
72 *
73 * Call use_env(flag) before either setupterm(), newterm(), or initscr().
74 */
75 static bool use_environment = TRUE;
76
77 static const char e_terminal[] =
78 "No memory for TERMINAL structure.\n";
79 static const char e_unknown[] =
80 "\"%s\": Unknown terminal type.\n";
81 static const char e_pathmax[] =
82 "\"%s\": terminfo database path too long.\n";
83
84
85 /*
86 * These globals are used so that macro arguments are evaluated
87 * exactly once
88 */
89 /* The downside is that it is not really thread-safe. Oh well... */
90 WINDOW *__w1;
91 chtype __cht1;
92 chtype __cht2;
93 cchar_t *__pcht1;
94 cchar_t *__pcht2;
95
96 /*
97 * Take the real command character out of the CC environment variable
98 * and substitute it in for the prototype given in 'command_character'.
99 */
100 static void
do_prototype(void)101 do_prototype(void)
102 {
103 int i, j;
104 char proto;
105 char *CC;
106
107 CC = getenv("CC");
108 proto = *command_character;
109
110 for (i = 0; i < __COUNT_STR; i++)
111 for (j = 0; cur_term->_str[i][j]; ++j)
112 if (cur_term->_str[i][j] == proto)
113 cur_term->_str[i][j] = *CC;
114 }
115
116 #define min(a, b) ((a) < (b) ? (a) : (b))
117
118 /*
119 * Return a number from a terminfo file. Numbers in a terminfo
120 * file are stored as two bytes with low-high ordering.
121 */
122 static short
getnum(int fd)123 getnum(int fd)
124 {
125 unsigned char bytes[2];
126
127 if (read(fd, bytes, 2) != 2)
128 return (SHRT_MIN);
129
130 return ((short) (bytes[0] + bytes[1] * 256));
131 }
132
133 /*
134 * MKS Header format for terminfo database files.
135 *
136 * The header consists of six short integers, stored using VAX/PDP style
137 * byte swapping (least-significant byte first). The integers are
138 *
139 * 1) magic number (octal 0432);
140 * 2) the size, in bytes, of the names sections;
141 * 3) the number of bytes in the boolean section;
142 * 4) the number of short integers in the numbers section;
143 * 5) the number of offsets (short integers) in the strings section;
144 * 6) the size, in bytes, of the string table.
145 *
146 * Between the boolean and number sections, a null byte is inserted, if
147 * necessary, to ensure that the number section begins on an even byte
148 * offset. All short integers are aligned on a short word boundary.
149 */
150
151 #define __TERMINFO_MAGIC 0432
152
153 typedef struct {
154 short magic;
155 short name_size;
156 short bool_count;
157 short num_count;
158 short str_count;
159 short str_size;
160 } terminfo_header_t;
161
162 /*
163 * Read the compiled terminfo entry in the given file into the
164 * structure pointed to by ptr, allocating space for the string
165 * table and placing its address in ptr->str_table.
166 */
167 int
__m_read_terminfo(const char * filename,TERMINAL * tp)168 __m_read_terminfo(const char *filename, TERMINAL *tp)
169 {
170 int fd;
171 short offset;
172 size_t i, len;
173 terminfo_header_t header;
174 unsigned char ch;
175
176 /* Get compiled terminfo file header. */
177 if ((fd = open(filename, 0)) < 0) {
178 goto error_1;
179 }
180
181 if ((header.magic = getnum(fd)) != __TERMINFO_MAGIC ||
182 (header.name_size = getnum(fd)) < 0 ||
183 (header.bool_count = getnum(fd)) < 0 ||
184 (header.num_count = getnum(fd)) < 0 ||
185 (header.str_count = getnum(fd)) < 0 ||
186 (header.str_size = getnum(fd)) < 0) {
187 goto error_2;
188 }
189
190
191 /* Allocate and fetch terminal names. */
192 len = min(127, header.name_size);
193 if ((tp->_names = (char *) malloc(len + 1)) == NULL)
194 goto error_2;
195 if (read(fd, tp->_names, len) != len)
196 goto error_3;
197 tp->_names[len] = '\0';
198
199 if (127 < header.name_size)
200 (void) lseek(fd, (off_t) (header.name_size - 127), SEEK_CUR);
201
202 /* Fetch booleans. */
203 len = min(__COUNT_BOOL, header.bool_count);
204 if (read(fd, tp->_bool, len) != len)
205 goto error_3;
206
207 if (__COUNT_BOOL < header.bool_count) {
208 (void) lseek(fd, (off_t) (header.bool_count - __COUNT_BOOL),
209 SEEK_CUR);
210 } else {
211 for (len = header.bool_count; len < __COUNT_BOOL; ++len)
212 tp->_bool[len] = 0;
213 }
214
215 /* Eat padding byte. */
216 if ((header.name_size + header.bool_count) % 2 != 0)
217 (void) read(fd, &ch, sizeof (ch));
218
219 /* Fetch numbers. */
220 len = min(__COUNT_NUM, header.num_count);
221 for (i = 0; i < len; ++i)
222 tp->_num[i] = getnum(fd);
223
224 if (__COUNT_NUM < header.num_count) {
225 (void) lseek(fd, (off_t) (2 *
226 (header.num_count - __COUNT_NUM)), SEEK_CUR);
227 } else {
228 for (len = header.num_count; len < __COUNT_NUM; ++len)
229 tp->_num[len] = -1;
230 }
231
232 /* Allocate and fetch strings. */
233 if ((tp->_str_table = (char *) malloc(header.str_size)) == NULL)
234 goto error_3;
235
236 /* Read in string offset section setting pointers to strings. */
237 len = min(__COUNT_STR, header.str_count);
238 for (i = 0; i < len; ++i) {
239 if ((offset = getnum(fd)) == SHRT_MIN)
240 goto error_4;
241
242 if (offset < 0)
243 tp->_str[i] = NULL;
244 else
245 tp->_str[i] = tp->_str_table + offset;
246 }
247
248 if (__COUNT_STR < header.str_count) {
249 (void) lseek(fd, (off_t) (2 *
250 (header.str_count - __COUNT_STR)), SEEK_CUR);
251 } else {
252 for (; i < __COUNT_STR; ++i)
253 tp->_str[i] = NULL;
254 }
255
256 if (read(fd, tp->_str_table, header.str_size) != header.str_size)
257 goto error_4;
258 (void) close(fd);
259
260 return (0);
261 error_4:
262 free(tp->_str_table);
263 error_3:
264 free(tp->_names);
265 error_2:
266 (void) close(fd);
267 error_1:
268 return (-1);
269 }
270
271 void
use_env(bool bf)272 use_env(bool bf)
273 {
274 use_environment = bf;
275 }
276
277 /*
278 * Set up terminal.
279 *
280 * Reads in the terminfo database pointed to by $TERMINFO env. var.
281 * for the given terminal, but does not set up the output virtualization
282 * structues used by CURSES. If the terminal name pointer is NULL,
283 * the $TERM env. var. is used for the terminal. All output is to
284 * the given file descriptor which is initialized for output.
285 *
286 * On error, if errret != NULL then setupterm() returns OK
287 * or ERR and stores a status value in the integer pointed to by
288 * errret. A status of 1 is normal, 0 means the terminal could
289 * not be found, and -1 means the terminfo database could not be
290 * found. If errret == NULL then setupterm() prints an error
291 * message upon and exit().
292 *
293 * On success, cur_term set to a terminfo structure and OK returned.
294 */
295 int
__m_setupterm(char * termname,int ifd,int ofd,int * err_return)296 __m_setupterm(char *termname, int ifd, int ofd, int *err_return)
297 {
298 int err_code = 1;
299 TERMINAL *old_term;
300 const char *err_msg;
301
302 /*
303 * It is possible to call setupterm() for multiple terminals,
304 * in which case we have to be able to restore cur_term in
305 * case of error.
306 */
307 old_term = cur_term;
308
309 cur_term = (TERMINAL *) calloc(1, sizeof (*cur_term));
310 if (cur_term == NULL) {
311 err_code = -1;
312 goto error;
313 }
314
315 if (isatty(cur_term->_ifd = ifd))
316 cur_term->_flags |= __TERM_ISATTY_IN;
317 if (isatty(cur_term->_ofd = ofd))
318 cur_term->_flags |= __TERM_ISATTY_OUT;
319
320 cur_term->_shell = (void *) calloc(1, sizeof (struct termios));
321 cur_term->_prog = (void *) calloc(1, sizeof (struct termios));
322 cur_term->_save = (void *) calloc(1, sizeof (struct termios));
323 cur_term->_actual = (void *) calloc(1, sizeof (struct termios));
324 cur_term->_term = NULL;
325 cur_term->_names = NULL;
326 cur_term->_str_table = NULL;
327 (void) def_shell_mode();
328 (void) def_prog_mode();
329 (void) __m_tty_get(PTERMIOS(_actual)); /* Synch cached value */
330
331 #ifdef ONLCR
332 if ((PTERMIOS(_prog)->c_oflag & (OPOST | ONLCR)) == (OPOST | ONLCR))
333 #else
334 if (PTERMIOS(_prog)->c_oflag & OPOST)
335 #endif
336 cur_term->_flags |= __TERM_NL_IS_CRLF;
337
338 (void) restartterm(termname, ofd, &err_code);
339 error:
340 switch (err_code) {
341 case -1:
342 err_msg = e_terminal;
343 break;
344 case 0:
345 err_msg = e_unknown;
346 break;
347 case 1:
348 break;
349 case 2:
350 err_msg = e_pathmax;
351 err_code = -1;
352 break;
353 }
354
355 if (err_return != NULL) {
356 *err_return = err_code;
357
358 if (err_code == 1) {
359 err_code = OK;
360 } else {
361 err_code = ERR;
362 free(cur_term);
363 cur_term = old_term;
364 }
365 } else if (err_code != 1) {
366 (void) fprintf(stderr, err_msg, termname);
367 exit(1);
368 }
369
370 return (err_code);
371 }
372
373 int
setupterm(char * term,int out,int * err)374 setupterm(char *term, int out, int *err)
375 {
376 int code;
377
378 code = __m_setupterm(term, STDIN_FILENO, out, err);
379
380 return (code);
381 }
382
383 int
del_curterm(TERMINAL * tp)384 del_curterm(TERMINAL *tp)
385 {
386 if (tp != NULL) {
387 if (cur_term == tp)
388 cur_term = NULL;
389 if (tp->_str_table != NULL)
390 free(tp->_str_table);
391 if (tp->_names != NULL)
392 free(tp->_names);
393 if (tp->_term != NULL)
394 free(tp->_term);
395 if (tp->_pair != (short (*)[2]) 0)
396 free(tp->_pair);
397 if (tp->_color != (short (*)[3]) 0)
398 free(tp->_color);
399 if (tp->_shell)
400 free(tp->_shell);
401 if (tp->_prog)
402 free(tp->_prog);
403 if (tp->_save)
404 free(tp->_save);
405 if (tp->_actual)
406 free(tp->_actual);
407 free(tp);
408 }
409
410 return (OK);
411 }
412
413 TERMINAL *
set_curterm(TERMINAL * tp)414 set_curterm(TERMINAL *tp)
415 {
416 TERMINAL *old;
417
418 old = cur_term;
419 cur_term = tp;
420
421 return (old);
422 }
423
424 int
restartterm(char * tm,int fd,int * err_return)425 restartterm(char *tm, int fd, int *err_return)
426 {
427 size_t len;
428 int err_code;
429 const char *err_msg, *terminfo;
430 char *old_names, *old_strings, *old_term, *filename;
431 static const char def_termname[] = M_TERM_NAME;
432 static const char def_terminfo[] = M_TERMINFO_DIR;
433
434 err_code = 1;
435 filename = NULL;
436 old_term = cur_term->_term;
437 old_names = cur_term->_names;
438 old_strings = cur_term->_str_table;
439
440 terminfo = getenv("TERMINFO");
441 if (terminfo == NULL || terminfo[0] == '\0') {
442 terminfo = def_terminfo;
443 } else {
444 terminfo = (const char *) strdup((char *) terminfo);
445 if (terminfo == NULL) {
446 /* Not really true... */
447 err_msg = e_terminal;
448 err_code = 2;
449 goto error;
450 }
451 }
452
453 if ((tm == NULL) &&
454 (((tm = getenv("TERM")) == NULL) || (*tm == '\0'))) {
455 tm = (char *)def_termname;
456 }
457
458 /* Remember the terminal name being loaded. */
459 cur_term->_term = strdup(tm);
460 if (cur_term->_term == NULL) {
461 err_msg = e_terminal;
462 err_code = 2;
463 goto error;
464 }
465
466 /* Length of path we're going to construct. */
467 len = strlen(terminfo) + 3 + strlen(tm);
468
469 if ((len > PATH_MAX) ||
470 ((filename = (char *)malloc(PATH_MAX + 1)) == NULL)) {
471 err_msg = e_pathmax;
472 err_code = 2;
473 goto error;
474 }
475
476 /* Construct terminfo filename. */
477 (void) sprintf(filename, "%s/%c/%s", terminfo, tm[0], tm);
478
479 /* Go looking for compiled terminal definition. */
480 if (__m_read_terminfo(filename, cur_term) < 0) {
481 /* Length of default terminfo path. */
482 len = strlen(def_terminfo) + 3 + strlen(tm);
483
484 if (len > PATH_MAX) {
485 err_msg = e_pathmax;
486 err_code = 2;
487 goto error;
488 }
489
490 (void) sprintf(filename, "%s/%c/%s", def_terminfo, tm[0], tm);
491
492 if (__m_read_terminfo(filename, cur_term) < 0) {
493 err_msg = e_unknown;
494 err_code = 0;
495 goto error;
496 }
497 }
498
499 if (use_environment) {
500 char *env;
501 #ifdef TIOCGWINSZ
502 /*
503 * Use ioctl(TIOCGWINSZ) to get row and column values. These
504 * values may override the default terminfo settings.
505 */
506 {
507 struct winsize wininfo;
508 if (ioctl(fd, TIOCGWINSZ, &wininfo) != -1) {
509 if (0 < wininfo.ws_col)
510 columns = wininfo.ws_col;
511 if (0 < wininfo.ws_row)
512 lines = wininfo.ws_row;
513 }
514 }
515 #endif /* TIOCGWINSZ */
516
517 /* Check to see is the user wants a particular size terminal. */
518 if ((env = getenv("LINES")) != NULL) {
519 int nlines = (int) strtol(env, (char **) 0, 10);
520 if (0 < nlines)
521 lines = nlines;
522 }
523 if ((env = getenv("COLUMNS")) != NULL) {
524 int ncolumns = (int) strtol(env, (char **) 0, 10);
525 if (0 < ncolumns)
526 columns = ncolumns;
527 }
528 }
529
530 if (command_character != NULL && getenv("CC") != NULL)
531 do_prototype();
532
533 /*
534 * If no_color_video is disabled, then assign it a value that
535 * permits all attributes in combination with colour.
536 */
537 if (no_color_video == -1)
538 no_color_video = 0;
539
540 __m_mvcur_cost();
541 error:
542 if (filename != NULL)
543 free(filename);
544
545 if (terminfo != def_terminfo)
546 free((void *) terminfo);
547
548 if (err_return != NULL) {
549 *err_return = err_code;
550
551 if (err_code == 1) {
552 err_code = OK;
553 } else {
554 err_code = ERR;
555 cur_term->_term = old_term;
556 cur_term->_names = old_names;
557 cur_term->_str_table = old_strings;
558 }
559 } else if (err_code != 1) {
560 (void) fprintf(stderr, err_msg, tm);
561 exit(1);
562 }
563
564 if (err_code == OK) {
565 if (old_names != NULL)
566 free(old_names);
567 if (old_strings != NULL)
568 free(old_strings);
569 if (old_term != NULL)
570 free(old_term);
571 }
572
573 return (err_code);
574 }
575
576 /*
577 * Get the termios setting for the terminal. Check the input
578 * file descriptor first, else the output file descriptor. If
579 * both input and output are both terminals, it is assumed that
580 * they refer to the same terminal.
581 */
582 int
__m_tty_get(struct termios * tp)583 __m_tty_get(struct termios *tp)
584 {
585 if (tcgetattr(cur_term->_ifd, tp) != 0) {
586 /*
587 * Input was not a terminal, possibly redirected.
588 * Check output instead.
589 */
590 if (tcgetattr(cur_term->_ofd, tp) != 0)
591 return (ERR);
592 }
593
594 return (OK);
595 }
596
597 int
__m_tty_set_prog_mode(void)598 __m_tty_set_prog_mode(void)
599 {
600 return (__m_tty_set(PTERMIOS(_prog)));
601 }
602
603 /*
604 * Restore the termios settings.
605 */
606 int
__m_tty_set(struct termios * tp)607 __m_tty_set(struct termios *tp)
608 {
609 int fd;
610 int rval;
611
612 if (cur_term->_flags & __TERM_ISATTY_OUT) {
613 fd = cur_term->_ofd;
614 } else if (cur_term->_flags & __TERM_ISATTY_IN) {
615 fd = cur_term->_ifd;
616 } else {
617 return (OK);
618 }
619 if (memcmp(tp, &cur_term->_actual, sizeof (struct termios)) == 0)
620 return (OK);
621
622 *PTERMIOS(_actual) = *tp;
623
624 rval = tcsetattr(fd, TCSADRAIN, tp) == 0 ? OK : ERR;
625
626 return (rval);
627 }
628
629 int
def_shell_mode(void)630 def_shell_mode(void)
631 {
632 return (__m_tty_get(PTERMIOS(_shell)));
633 }
634
635 int
def_prog_mode(void)636 def_prog_mode(void)
637 {
638 return (__m_tty_get(PTERMIOS(_prog)));
639 }
640
641 int
reset_shell_mode(void)642 reset_shell_mode(void)
643 {
644 return (__m_tty_set(PTERMIOS(_shell)));
645 }
646
647 int
reset_prog_mode(void)648 reset_prog_mode(void)
649 {
650 return (__m_tty_set_prog_mode());
651 }
652