1 /* $NetBSD: setterm.c,v 1.38 2003/10/21 00:30:05 fvdl Exp $ */ 2 3 /* 4 * Copyright (c) 1981, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)setterm.c 8.8 (Berkeley) 10/25/94"; 36 #else 37 __RCSID("$NetBSD: setterm.c,v 1.38 2003/10/21 00:30:05 fvdl Exp $"); 38 #endif 39 #endif /* not lint */ 40 41 #include <sys/ioctl.h> /* TIOCGWINSZ on old systems. */ 42 43 #include <stdlib.h> 44 #include <string.h> 45 #include <termios.h> 46 #include <unistd.h> 47 48 #include "curses.h" 49 #include "curses_private.h" 50 51 static int zap(SCREEN *screen); 52 53 static int does_esc_m(char *cap); 54 static int does_ctrl_o(char *cap); 55 56 struct tinfo *_cursesi_genbuf; 57 58 static char *sflags[] = { 59 /* am bs cc da eo */ 60 &__tc_am, &__tc_bs, &__tc_cc, &__tc_da, &__tc_eo, 61 /* hc hl in mi ms */ 62 &__tc_hc, &__tc_hl, &__tc_in, &__tc_mi, &__tc_ms, 63 /* nc ns os ul ut */ 64 &__tc_nc, &__tc_ns, &__tc_os, &__tc_ul, &__tc_ut, 65 /* xb xn xt xs xx */ 66 &__tc_xb, &__tc_xn, &__tc_xt, &__tc_xs, &__tc_xx 67 }; 68 69 static int *svals[] = { 70 /* pa Co NC */ 71 &__tc_pa, &__tc_Co, &__tc_NC 72 }; 73 74 static char *_PC, 75 **sstrs[] = { 76 /* AB ac ae AF AL */ 77 &__tc_AB, &__tc_ac, &__tc_ae, &__tc_AF, &__tc_AL, 78 /* al as bc bl bt */ 79 &__tc_al, &__tc_as, &__tc_bc, &__tc_bl, &__tc_bt, 80 /* cd ce cl cm cr */ 81 &__tc_cd, &__tc_ce, &__tc_cl, &__tc_cm, &__tc_cr, 82 /* cs dc DL dl dm */ 83 &__tc_cs, &__tc_dc, &__tc_DL, &__tc_dl, &__tc_dm, 84 /* DO do eA ed ei */ 85 &__tc_DO, &__tc_do, &__tc_eA, &__tc_ed, &__tc_ei, 86 /* ho Ic ic im Ip */ 87 &__tc_ho, &__tc_Ic, &__tc_ic, &__tc_im, &__tc_Ip, 88 /* ip k0 k1 k2 k3 */ 89 &__tc_ip, &__tc_k0, &__tc_k1, &__tc_k2, &__tc_k3, 90 /* k4 k5 k6 k7 k8 */ 91 &__tc_k4, &__tc_k5, &__tc_k6, &__tc_k7, &__tc_k8, 92 /* k9 kd ke kh kl */ 93 &__tc_k9, &__tc_kd, &__tc_ke, &__tc_kh, &__tc_kl, 94 /* kr ks ku LE ll */ 95 &__tc_kr, &__tc_ks, &__tc_ku, &__tc_LE, &__tc_ll, 96 /* ma mb md me mh */ 97 &__tc_ma, &__tc_mb, &__tc_md, &__tc_me, &__tc_mh, 98 /* mk mm mo mp mr */ 99 &__tc_mk, &__tc_mm, &__tc_mo, &__tc_mp, &__tc_mr, 100 /* nd nl oc op pc */ 101 &__tc_nd, &__tc_nl, &__tc_oc, &__tc_op, &_PC, 102 /* rc RI sc Sb se */ 103 &__tc_rc, &__tc_RI, &__tc_Sb, &__tc_sc, &__tc_se, 104 /* SF Sf sf so sp */ 105 &__tc_SF, &__tc_Sf, &__tc_sf, &__tc_so, &__tc_sp, 106 /* SR sr ta te ti */ 107 &__tc_SR, &__tc_sr, &__tc_ta, &__tc_te, &__tc_ti, 108 /* uc ue UP up us */ 109 &__tc_uc, &__tc_ue, &__tc_UP, &__tc_up, &__tc_us, 110 /* vb ve vi vs */ 111 &__tc_vb, &__tc_ve, &__tc_vi, &__tc_vs 112 }; 113 114 attr_t __mask_op, __mask_me, __mask_ue, __mask_se; 115 116 int 117 setterm(char *type) 118 { 119 return _cursesi_setterm(type, _cursesi_screen); 120 } 121 122 int 123 _cursesi_setterm(char *type, SCREEN *screen) 124 { 125 int unknown; 126 struct winsize win; 127 char *p; 128 char cm_buff[64]; 129 130 if (type[0] == '\0') 131 type = "xx"; 132 unknown = 0; 133 if (t_getent(&screen->cursesi_genbuf, type) != 1) { 134 unknown++; 135 } 136 #ifdef DEBUG 137 __CTRACE("setterm: tty = %s\n", type); 138 #endif 139 140 /* Try TIOCGWINSZ, and, if it fails, the termcap entry. */ 141 if (ioctl(fileno(screen->outfd), TIOCGWINSZ, &win) != -1 && 142 win.ws_row != 0 && win.ws_col != 0) { 143 screen->LINES = win.ws_row; 144 screen->COLS = win.ws_col; 145 } else { 146 if (unknown) { 147 screen->LINES = -1; 148 screen->COLS = -1; 149 } else { 150 screen->LINES = t_getnum(screen->cursesi_genbuf, "li"); 151 screen->COLS = t_getnum(screen->cursesi_genbuf, "co"); 152 } 153 154 } 155 156 /* POSIX 1003.2 requires that the environment override. */ 157 if ((p = getenv("LINES")) != NULL) 158 screen->LINES = (int) strtol(p, NULL, 10); 159 if ((p = getenv("COLUMNS")) != NULL) 160 screen->COLS = (int) strtol(p, NULL, 10); 161 162 /* 163 * Want cols > 4, otherwise things will fail. 164 */ 165 if (screen->COLS <= 4) 166 return (ERR); 167 168 LINES = screen->LINES; 169 COLS = screen->COLS; 170 171 #ifdef DEBUG 172 __CTRACE("setterm: LINES = %d, COLS = %d\n", LINES, COLS); 173 #endif 174 if (!unknown) { 175 if (zap(screen) == ERR) /* Get terminal description.*/ 176 return ERR; 177 } 178 179 /* If we can't tab, we can't backtab, either. */ 180 if (!screen->GT) 181 screen->tc_bt = NULL; 182 183 /* 184 * Test for cursor motion capability. 185 * 186 */ 187 if (t_goto(NULL, screen->tc_cm, 0, 0, cm_buff, sizeof(cm_buff) - 1) < 0) { 188 screen->CA = 0; 189 screen->tc_cm = 0; 190 } else 191 screen->CA = 1; 192 193 /* 194 * set the pad char, only take the first char of the pc capability 195 * as this is all we can use. 196 */ 197 screen->pad_char = screen->tc_pc ? screen->tc_pc[0] : 0; 198 199 /* Get full name of terminal */ 200 if (unknown) { 201 strcpy(screen->ttytype, "dumb"); 202 } else { 203 char *tcptr; 204 size_t limit = 0; 205 if (t_getterm(screen->cursesi_genbuf, 0, &limit)) 206 return ERR; 207 if ((tcptr = malloc(limit + 1)) == NULL) 208 return ERR; 209 if (t_getterm(screen->cursesi_genbuf, &tcptr, 0) < 0) 210 return ERR; 211 __longname(tcptr, screen->ttytype); 212 free(tcptr); 213 } 214 215 /* If no scrolling commands, no quick change. */ 216 screen->noqch = 217 (screen->tc_cs == NULL || screen->tc_ho == NULL || 218 (screen->tc_SF == NULL && screen->tc_sf == NULL) || 219 (screen->tc_SR == NULL && screen->tc_sr == NULL)) && 220 ((screen->tc_AL == NULL && screen->tc_al == NULL) || 221 (screen->tc_DL == NULL && screen->tc_dl == NULL)); 222 223 /* 224 * Precalculate conflict info for color/attribute end commands. 225 */ 226 screen->mask_op = __ATTRIBUTES & ~__COLOR; 227 if (screen->tc_op != NULL) { 228 if (does_esc_m(screen->tc_op)) 229 screen->mask_op &= 230 ~(__STANDOUT | __UNDERSCORE | __TERMATTR); 231 else { 232 if (screen->tc_se != NULL && 233 !strcmp(screen->tc_op, screen->tc_se)) 234 screen->mask_op &= ~__STANDOUT; 235 if (screen->tc_ue != NULL && 236 !strcmp(screen->tc_op, screen->tc_ue)) 237 screen->mask_op &= ~__UNDERSCORE; 238 if (screen->tc_me != NULL && 239 !strcmp(screen->tc_op, screen->tc_me)) 240 screen->mask_op &= ~__TERMATTR; 241 } 242 } 243 /* 244 * Assume that "me" turns off all attributes apart from ACS. 245 * It might turn off ACS, so check for that. 246 */ 247 if (screen->tc_me != NULL && does_ctrl_o(screen->tc_me)) 248 screen->mask_me = 0; 249 else 250 screen->mask_me = __ALTCHARSET; 251 252 /* Check what turning off the attributes also turns off */ 253 screen->mask_ue = __ATTRIBUTES & ~__UNDERSCORE; 254 if (screen->tc_ue != NULL) { 255 if (does_esc_m(screen->tc_ue)) 256 screen->mask_ue &= 257 ~(__STANDOUT | __TERMATTR | __COLOR); 258 else { 259 if (screen->tc_se != NULL && 260 !strcmp(screen->tc_ue, screen->tc_se)) 261 screen->mask_ue &= ~__STANDOUT; 262 if (screen->tc_me != NULL && 263 !strcmp(screen->tc_ue, screen->tc_me)) 264 screen->mask_ue &= ~__TERMATTR; 265 if (screen->tc_op != NULL && 266 !strcmp(screen->tc_ue, screen->tc_op)) 267 screen->mask_ue &= ~__COLOR; 268 } 269 } 270 screen->mask_se = __ATTRIBUTES & ~__STANDOUT; 271 if (screen->tc_se != NULL) { 272 if (does_esc_m(screen->tc_se)) 273 screen->mask_se &= 274 ~(__UNDERSCORE | __TERMATTR | __COLOR); 275 else { 276 if (screen->tc_ue != NULL && 277 !strcmp(screen->tc_se, screen->tc_ue)) 278 screen->mask_se &= ~__UNDERSCORE; 279 if (screen->tc_me != NULL && 280 !strcmp(screen->tc_se, screen->tc_me)) 281 screen->mask_se &= ~__TERMATTR; 282 if (screen->tc_op != NULL && 283 !strcmp(screen->tc_se, screen->tc_op)) 284 screen->mask_se &= ~__COLOR; 285 } 286 } 287 288 return (unknown ? ERR : OK); 289 } 290 291 /* 292 * _cursesi_resetterm -- 293 * Copy the terminal instance data for the given screen to the global 294 * variables. 295 */ 296 void 297 _cursesi_resetterm(SCREEN *screen) 298 { 299 char ***sp, **scr_sp; 300 int **vp, *scr_vp, i; 301 char **fp, *scr_fp; 302 303 LINES = screen->LINES; 304 COLS = screen->COLS; 305 306 /* reset terminal capabilities */ 307 fp = sflags; 308 scr_fp = &screen->tc_am; 309 for (i = 0; i < screen->flag_count; i++) 310 *(*fp++) = *scr_fp++; 311 312 vp = svals; 313 scr_vp = &screen->tc_pa; 314 for (i = 0; i < screen->int_count; i++) 315 *(*vp++) = *scr_vp++; 316 317 sp = sstrs; 318 scr_sp = &screen->tc_AB; 319 for (i = 0; i < screen->str_count; i++) 320 *(*sp++) = *scr_sp++; 321 322 __GT = screen->GT; 323 __CA = screen->CA; 324 325 PC = screen->pad_char; 326 327 __noqch = screen->noqch; 328 __mask_op = screen->mask_op; 329 __mask_me = screen->mask_me; 330 __mask_ue = screen->mask_ue; 331 __mask_se = screen->mask_se; 332 } 333 334 /* 335 * zap -- 336 * Gets all the terminal flags from the termcap database. 337 */ 338 static int 339 zap(SCREEN *screen) 340 { 341 const char *nampstr, *namp; 342 char **sp; 343 int *vp; 344 char *fp; 345 char tmp[3]; 346 #ifdef DEBUG 347 char *cp; 348 #endif 349 tmp[2] = '\0'; 350 351 namp = "ambsccdaeohchlinmimsncnsosulutxbxnxtxsxx"; 352 fp = &screen->tc_am; 353 screen->flag_count = 0; 354 do { 355 *tmp = *namp; 356 *(tmp + 1) = *(namp + 1); 357 *fp++ = t_getflag(screen->cursesi_genbuf, tmp); 358 #ifdef DEBUG 359 __CTRACE("%2.2s = %s\n", namp, fp[-1] ? "TRUE" : "FALSE"); 360 #endif 361 namp += 2; 362 screen->flag_count++; 363 } while (*namp); 364 namp = "paCoNC"; 365 vp = &screen->tc_pa; 366 screen->int_count = 0; 367 do { 368 *tmp = *namp; 369 *(tmp + 1) = *(namp + 1); 370 *vp++ = t_getnum(screen->cursesi_genbuf, tmp); 371 #ifdef DEBUG 372 __CTRACE("%2.2s = %d\n", namp, vp[-1]); 373 #endif 374 namp += 2; 375 screen->int_count++; 376 } while (*namp); 377 378 nampstr = "ABacaeAFALalasbcblbtcdceclcmcrcsdcDLdldmDOdoeAedeihoIcicimIpipk0k1k2k3k4k5k6k7k8k9kdkekhklkrkskuLEllmambmdmemhmkmmmompmrndnlocoppcrcRISbscseSFSfsfsospSRsrtatetiucueUPupusvbvevivs"; 379 380 namp = nampstr; 381 sp = &screen->tc_AB; 382 screen->str_count = 0; 383 do { 384 *tmp = *namp; 385 *(tmp + 1) = *(namp + 1); 386 *sp++ = t_agetstr(screen->cursesi_genbuf, tmp); 387 #ifdef DEBUG 388 __CTRACE("%2.2s = %s", namp, sp[-1] == NULL ? "NULL\n" : "\""); 389 if (sp[-1] != NULL) { 390 for (cp = sp[-1]; *cp; cp++) 391 __CTRACE("%s", unctrl(*cp)); 392 __CTRACE("\"\n"); 393 } 394 #endif 395 namp += 2; 396 screen->str_count++; 397 } while (*namp); 398 if (screen->tc_xs) 399 screen->tc_so = screen->tc_se = NULL; 400 else { 401 if (t_getnum(screen->cursesi_genbuf, "sg") > 0) 402 screen->tc_so = NULL; 403 if (t_getnum(screen->cursesi_genbuf, "ug") > 0) 404 screen->tc_us = NULL; 405 if (!screen->tc_so && screen->tc_us) { 406 screen->tc_so = screen->tc_us; 407 screen->tc_se = screen->tc_ue; 408 } 409 } 410 411 return OK; 412 } 413 414 /* 415 * getcap -- 416 * Return a capability from termcap. 417 */ 418 char * 419 getcap(char *name) 420 { 421 return (t_agetstr(_cursesi_genbuf, name)); 422 } 423 424 /* 425 * does_esc_m -- 426 * A hack for xterm-like terminals where "\E[m" or "\E[0m" will turn off 427 * other attributes, so we check for this in the capability passed to us. 428 * Note that we can't just do simple string comparison, as the capability 429 * may contain multiple, ';' separated sequence parts. 430 */ 431 static int 432 does_esc_m(char *cap) 433 { 434 #define WAITING 0 435 #define PARSING 1 436 #define NUMBER 2 437 #define FOUND 4 438 char *capptr; 439 int seq; 440 441 #ifdef DEBUG 442 __CTRACE("does_esc_m: Checking %s\n", cap); 443 #endif 444 /* Is it just "\E[m" or "\E[0m"? */ 445 if (!strcmp(cap, "\x1b[m") || !strcmp(cap, "\x1b[0m")) 446 return 1; 447 448 /* Does it contain a "\E[...m" sequence? */ 449 if (strlen(cap) < 4) 450 return 0; 451 capptr = cap; 452 seq = WAITING; 453 while (*capptr != 0) { 454 switch (seq) { 455 /* Start of sequence */ 456 case WAITING: 457 if (!strncmp(capptr, "\x1b[", 2)) { 458 capptr+=2; 459 if (*capptr == 'm') 460 return 1; 461 else { 462 seq=PARSING; 463 continue; 464 } 465 } 466 break; 467 /* Looking for '0' */ 468 case PARSING: 469 if (*capptr == '0') 470 seq=FOUND; 471 else if (*capptr > '0' && *capptr <= '9') 472 seq=NUMBER; 473 else if (*capptr != ';') 474 seq=WAITING; 475 break; 476 /* Some other number */ 477 case NUMBER: 478 if (*capptr == ';') 479 seq=PARSING; 480 else if (!(*capptr >= '0' && *capptr <= '9')) 481 seq=WAITING; 482 break; 483 /* Found a '0' */ 484 case FOUND: 485 if (*capptr == 'm') 486 return 1; 487 else if (!((*capptr >= '0' && *capptr <= '9') || 488 *capptr == ';')) 489 seq=WAITING; 490 break; 491 default: 492 break; 493 } 494 capptr++; 495 } 496 return 0; 497 } 498 499 /* 500 * does_ctrl_o -- 501 * A hack for vt100/xterm-like terminals where the "me" capability also 502 * unsets acs (i.e. it contains the character '\017'). 503 */ 504 static int 505 does_ctrl_o(char *cap) 506 { 507 char *capptr = cap; 508 509 #ifdef DEBUG 510 __CTRACE("does_ctrl_o: Looping on %s\n", capptr); 511 #endif 512 while (*capptr != 0) { 513 if (*capptr == '\x0f') 514 return 1; 515 capptr++; 516 } 517 return 0; 518 } 519