1 /*- 2 * Copyright (c) 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * Copyright (c) 1993, 1994, 1995, 1996 5 * Keith Bostic. All rights reserved. 6 * 7 * See the LICENSE file for redistribution information. 8 */ 9 10 #include "config.h" 11 12 #ifndef lint 13 static const char sccsid[] = "$Id: cl_term.c,v 10.35 2015/04/08 02:12:11 zy Exp $"; 14 #endif /* not lint */ 15 16 #include <sys/types.h> 17 #include <sys/ioctl.h> 18 #include <sys/queue.h> 19 #include <sys/stat.h> 20 21 #include <bitstring.h> 22 #include <errno.h> 23 #include <limits.h> 24 #include <signal.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #ifdef HAVE_TERM_H 29 #include <term.h> 30 #endif 31 #include <termios.h> 32 #include <unistd.h> 33 34 #include "../common/common.h" 35 #include "cl.h" 36 37 static int cl_pfmap(SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t); 38 39 /* 40 * XXX 41 * THIS REQUIRES THAT ALL SCREENS SHARE A TERMINAL TYPE. 42 */ 43 typedef struct _tklist { 44 char *ts; /* Key's termcap string. */ 45 char *output; /* Corresponding vi command. */ 46 char *name; /* Name. */ 47 u_char value; /* Special value (for lookup). */ 48 } TKLIST; 49 static TKLIST const c_tklist[] = { /* Command mappings. */ 50 {"kil1", "O", "insert line"}, 51 {"kdch1", "x", "delete character"}, 52 {"kcud1", "j", "cursor down"}, 53 {"kel", "D", "delete to eol"}, 54 {"kind", "\004", "scroll down"}, /* ^D */ 55 {"kll", "$", "go to eol"}, 56 {"kend", "$", "go to eol"}, 57 {"khome", "^", "go to sol"}, 58 {"kich1", "i", "insert at cursor"}, 59 {"kdl1", "dd", "delete line"}, 60 {"kcub1", "h", "cursor left"}, 61 {"knp", "\006", "page down"}, /* ^F */ 62 {"kpp", "\002", "page up"}, /* ^B */ 63 {"kri", "\025", "scroll up"}, /* ^U */ 64 {"ked", "dG", "delete to end of screen"}, 65 {"kcuf1", "l", "cursor right"}, 66 {"kcuu1", "k", "cursor up"}, 67 {NULL}, 68 }; 69 static TKLIST const m1_tklist[] = { /* Input mappings (lookup). */ 70 {NULL}, 71 }; 72 static TKLIST const m2_tklist[] = { /* Input mappings (set or delete). */ 73 {"kcud1", "\033ja", "cursor down"}, /* ^[ja */ 74 {"kcub1", "\033ha", "cursor left"}, /* ^[ha */ 75 {"kcuu1", "\033ka", "cursor up"}, /* ^[ka */ 76 {"kcuf1", "\033la", "cursor right"}, /* ^[la */ 77 {NULL}, 78 }; 79 80 /* 81 * cl_term_init -- 82 * Initialize the special keys defined by the termcap/terminfo entry. 83 * 84 * PUBLIC: int cl_term_init(SCR *); 85 */ 86 int 87 cl_term_init(SCR *sp) 88 { 89 KEYLIST *kp; 90 SEQ *qp; 91 TKLIST const *tkp; 92 char *t; 93 CHAR_T name[60]; 94 CHAR_T output[5]; 95 CHAR_T ts[20]; 96 CHAR_T *wp; 97 size_t wlen; 98 99 /* Command mappings. */ 100 for (tkp = c_tklist; tkp->name != NULL; ++tkp) { 101 if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1) 102 continue; 103 CHAR2INT(sp, tkp->name, strlen(tkp->name), wp, wlen); 104 MEMCPY(name, wp, wlen); 105 CHAR2INT(sp, t, strlen(t), wp, wlen); 106 MEMCPY(ts, wp, wlen); 107 CHAR2INT(sp, tkp->output, strlen(tkp->output), wp, wlen); 108 MEMCPY(output, wp, wlen); 109 if (seq_set(sp, name, strlen(tkp->name), ts, strlen(t), 110 output, strlen(tkp->output), SEQ_COMMAND, 111 SEQ_NOOVERWRITE | SEQ_SCREEN)) 112 return (1); 113 } 114 115 /* Input mappings needing to be looked up. */ 116 for (tkp = m1_tklist; tkp->name != NULL; ++tkp) { 117 if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1) 118 continue; 119 for (kp = keylist;; ++kp) 120 if (kp->value == tkp->value) 121 break; 122 if (kp == NULL) 123 continue; 124 CHAR2INT(sp, tkp->name, strlen(tkp->name), wp, wlen); 125 MEMCPY(name, wp, wlen); 126 CHAR2INT(sp, t, strlen(t), wp, wlen); 127 MEMCPY(ts, wp, wlen); 128 output[0] = (UCHAR_T)kp->ch; 129 if (seq_set(sp, name, strlen(tkp->name), ts, strlen(t), 130 output, 1, SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN)) 131 return (1); 132 } 133 134 /* Input mappings that are already set or are text deletions. */ 135 for (tkp = m2_tklist; tkp->name != NULL; ++tkp) { 136 if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1) 137 continue; 138 /* 139 * !!! 140 * Some terminals' <cursor_left> keys send single <backspace> 141 * characters. This is okay in command mapping, but not okay 142 * in input mapping. That combination is the only one we'll 143 * ever see, hopefully, so kluge it here for now. 144 */ 145 if (!strcmp(t, "\b")) 146 continue; 147 if (tkp->output == NULL) { 148 CHAR2INT(sp, tkp->name, strlen(tkp->name), wp, wlen); 149 MEMCPY(name, wp, wlen); 150 CHAR2INT(sp, t, strlen(t), wp, wlen); 151 MEMCPY(ts, wp, wlen); 152 if (seq_set(sp, name, strlen(tkp->name), 153 ts, strlen(t), NULL, 0, 154 SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN)) 155 return (1); 156 } else { 157 CHAR2INT(sp, tkp->name, strlen(tkp->name), wp, wlen); 158 MEMCPY(name, wp, wlen); 159 CHAR2INT(sp, t, strlen(t), wp, wlen); 160 MEMCPY(ts, wp, wlen); 161 CHAR2INT(sp, tkp->output, strlen(tkp->output), wp, wlen); 162 MEMCPY(output, wp, wlen); 163 if (seq_set(sp, name, strlen(tkp->name), 164 ts, strlen(t), output, strlen(tkp->output), 165 SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN)) 166 return (1); 167 } 168 } 169 170 /* 171 * Rework any function key mappings that were set before the 172 * screen was initialized. 173 */ 174 SLIST_FOREACH(qp, sp->gp->seqq, q) 175 if (F_ISSET(qp, SEQ_FUNCMAP)) 176 (void)cl_pfmap(sp, qp->stype, 177 qp->input, qp->ilen, qp->output, qp->olen); 178 return (0); 179 } 180 181 /* 182 * cl_term_end -- 183 * End the special keys defined by the termcap/terminfo entry. 184 * 185 * PUBLIC: int cl_term_end(GS *); 186 */ 187 int 188 cl_term_end(GS *gp) 189 { 190 SEQ *qp, *nqp, *pre_qp = NULL; 191 192 /* Delete screen specific mappings. */ 193 SLIST_FOREACH_SAFE(qp, gp->seqq, q, nqp) 194 if (F_ISSET(qp, SEQ_SCREEN)) { 195 if (qp == SLIST_FIRST(gp->seqq)) 196 SLIST_REMOVE_HEAD(gp->seqq, q); 197 else 198 SLIST_REMOVE_AFTER(pre_qp, q); 199 (void)seq_free(qp); 200 } else 201 pre_qp = qp; 202 return (0); 203 } 204 205 /* 206 * cl_fmap -- 207 * Map a function key. 208 * 209 * PUBLIC: int cl_fmap(SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t); 210 */ 211 int 212 cl_fmap(SCR *sp, seq_t stype, CHAR_T *from, size_t flen, CHAR_T *to, size_t tlen) 213 { 214 /* Ignore until the screen is running, do the real work then. */ 215 if (F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_SCR_VI)) 216 return (0); 217 if (F_ISSET(sp, SC_EX) && !F_ISSET(sp, SC_SCR_EX)) 218 return (0); 219 220 return (cl_pfmap(sp, stype, from, flen, to, tlen)); 221 } 222 223 /* 224 * cl_pfmap -- 225 * Map a function key (private version). 226 */ 227 static int 228 cl_pfmap(SCR *sp, seq_t stype, CHAR_T *from, size_t flen, CHAR_T *to, size_t tlen) 229 { 230 size_t nlen; 231 char *p; 232 char name[64]; 233 CHAR_T keyname[64]; 234 CHAR_T ts[20]; 235 CHAR_T *wp; 236 size_t wlen; 237 238 (void)snprintf(name, sizeof(name), "kf%d", 239 (int)STRTOL(from+1,NULL,10)); 240 if ((p = tigetstr(name)) == NULL || 241 p == (char *)-1 || strlen(p) == 0) 242 p = NULL; 243 if (p == NULL) { 244 msgq_wstr(sp, M_ERR, from, "233|This terminal has no %s key"); 245 return (1); 246 } 247 248 nlen = SPRINTF(keyname, 249 SIZE(keyname), L("function key %d"), 250 (int)STRTOL(from+1,NULL,10)); 251 CHAR2INT(sp, p, strlen(p), wp, wlen); 252 MEMCPY(ts, wp, wlen); 253 return (seq_set(sp, keyname, nlen, 254 ts, strlen(p), to, tlen, stype, SEQ_NOOVERWRITE | SEQ_SCREEN)); 255 } 256 257 /* 258 * cl_optchange -- 259 * Curses screen specific "option changed" routine. 260 * 261 * PUBLIC: int cl_optchange(SCR *, int, char *, u_long *); 262 */ 263 int 264 cl_optchange(SCR *sp, int opt, char *str, u_long *valp) 265 { 266 CL_PRIVATE *clp; 267 268 clp = CLP(sp); 269 270 switch (opt) { 271 case O_TERM: 272 F_CLR(sp, SC_SCR_EX | SC_SCR_VI); 273 /* FALLTHROUGH */ 274 case O_COLUMNS: 275 case O_LINES: 276 /* 277 * Changing the terminal type requires that we reinitialize 278 * curses, while resizing does not. 279 */ 280 F_SET(sp->gp, G_SRESTART); 281 break; 282 case O_MESG: 283 (void)cl_omesg(sp, clp, *valp); 284 break; 285 case O_WINDOWNAME: 286 if (*valp) { 287 F_SET(clp, CL_RENAME_OK); 288 289 /* 290 * If the screen is live, i.e. we're not reading the 291 * .exrc file, update the window. 292 */ 293 if (sp->frp != NULL && sp->frp->name != NULL) 294 (void)cl_rename(sp, sp->frp->name, 1); 295 } else { 296 F_CLR(clp, CL_RENAME_OK); 297 298 (void)cl_rename(sp, NULL, 0); 299 } 300 break; 301 } 302 return (0); 303 } 304 305 /* 306 * cl_omesg -- 307 * Turn the tty write permission on or off. 308 * 309 * PUBLIC: int cl_omesg(SCR *, CL_PRIVATE *, int); 310 */ 311 int 312 cl_omesg(SCR *sp, CL_PRIVATE *clp, int on) 313 { 314 struct stat sb; 315 char *tty; 316 317 /* Find the tty, get the current permissions. */ 318 if ((tty = ttyname(STDERR_FILENO)) == NULL) { 319 if (sp != NULL) 320 msgq(sp, M_SYSERR, "stderr"); 321 return (1); 322 } 323 if (stat(tty, &sb) < 0) { 324 if (sp != NULL) 325 msgq(sp, M_SYSERR, "%s", tty); 326 return (1); 327 } 328 329 /* Save the original status if it's unknown. */ 330 if (clp->tgw == TGW_UNKNOWN) 331 clp->tgw = sb.st_mode & S_IWGRP ? TGW_SET : TGW_UNSET; 332 333 /* Toggle the permissions. */ 334 if (on) { 335 if (chmod(tty, sb.st_mode | S_IWGRP) < 0) { 336 if (sp != NULL) 337 msgq(sp, M_SYSERR, 338 "046|messages not turned on: %s", tty); 339 return (1); 340 } 341 } else 342 if (chmod(tty, sb.st_mode & ~S_IWGRP) < 0) { 343 if (sp != NULL) 344 msgq(sp, M_SYSERR, 345 "045|messages not turned off: %s", tty); 346 return (1); 347 } 348 return (0); 349 } 350 351 /* 352 * cl_ssize -- 353 * Return the terminal size. 354 * 355 * PUBLIC: int cl_ssize(SCR *, int, size_t *, size_t *, int *); 356 */ 357 int 358 cl_ssize(SCR *sp, int sigwinch, size_t *rowp, size_t *colp, int *changedp) 359 { 360 struct winsize win; 361 size_t col, row; 362 int rval; 363 char *p; 364 365 /* Assume it's changed. */ 366 if (changedp != NULL) 367 *changedp = 1; 368 369 /* 370 * !!! 371 * sp may be NULL. 372 * 373 * Get the screen rows and columns. If the values are wrong, it's 374 * not a big deal -- as soon as the user sets them explicitly the 375 * environment will be set and the screen package will use the new 376 * values. 377 * 378 * Try TIOCGWINSZ. 379 */ 380 row = col = 0; 381 if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1) { 382 row = win.ws_row; 383 col = win.ws_col; 384 } 385 /* If here because of suspend or a signal, only trust TIOCGWINSZ. */ 386 if (sigwinch) { 387 /* 388 * Somebody didn't get TIOCGWINSZ right, or has suspend 389 * without window resizing support. The user just lost, 390 * but there's nothing we can do. 391 */ 392 if (row == 0 || col == 0) { 393 if (changedp != NULL) 394 *changedp = 0; 395 return (0); 396 } 397 398 /* 399 * SunOS systems deliver SIGWINCH when windows are uncovered 400 * as well as when they change size. In addition, we call 401 * here when continuing after being suspended since the window 402 * may have changed size. Since we don't want to background 403 * all of the screens just because the window was uncovered, 404 * ignore the signal if there's no change. 405 */ 406 if (sp != NULL && 407 row == O_VAL(sp, O_LINES) && col == O_VAL(sp, O_COLUMNS)) { 408 if (changedp != NULL) 409 *changedp = 0; 410 return (0); 411 } 412 413 if (rowp != NULL) 414 *rowp = row; 415 if (colp != NULL) 416 *colp = col; 417 return (0); 418 } 419 420 /* 421 * !!! 422 * If TIOCGWINSZ failed, or had entries of 0, try termcap. This 423 * routine is called before any termcap or terminal information 424 * has been set up. If there's no TERM environmental variable set, 425 * let it go, at least ex can run. 426 */ 427 if (row == 0 || col == 0) { 428 if ((p = getenv("TERM")) == NULL) 429 goto noterm; 430 if (row == 0) 431 if ((rval = tigetnum("lines")) < 0) 432 msgq(sp, M_SYSERR, "tigetnum: lines"); 433 else 434 row = rval; 435 if (col == 0) 436 if ((rval = tigetnum("cols")) < 0) 437 msgq(sp, M_SYSERR, "tigetnum: cols"); 438 else 439 col = rval; 440 } 441 442 /* If nothing else, well, it's probably a VT100. */ 443 noterm: if (row == 0) 444 row = 24; 445 if (col == 0) 446 col = 80; 447 448 /* 449 * !!! 450 * POSIX 1003.2 requires the environment to override everything. 451 * Often, people can get nvi to stop messing up their screen by 452 * deleting the LINES and COLUMNS environment variables from their 453 * dot-files. 454 */ 455 if ((p = getenv("LINES")) != NULL) 456 row = strtol(p, NULL, 10); 457 if ((p = getenv("COLUMNS")) != NULL) 458 col = strtol(p, NULL, 10); 459 460 if (rowp != NULL) 461 *rowp = row; 462 if (colp != NULL) 463 *colp = col; 464 return (0); 465 } 466 467 /* 468 * cl_putchar -- 469 * Function version of putchar, for tputs. 470 * 471 * PUBLIC: int cl_putchar(int); 472 */ 473 int 474 cl_putchar(int ch) 475 { 476 return (putchar(ch)); 477 } 478