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.34 2013/12/07 16:21:14 wjenkner 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 __P((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 __P((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 __P((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 __P((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 __P((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_COLUMNS: 272 case O_LINES: 273 case O_TERM: 274 /* 275 * Changing the columns, lines or terminal require that 276 * we restart the screen. 277 */ 278 F_SET(sp->gp, G_SRESTART); 279 F_CLR(sp, SC_SCR_EX | SC_SCR_VI); 280 break; 281 case O_MESG: 282 (void)cl_omesg(sp, clp, *valp); 283 break; 284 case O_WINDOWNAME: 285 if (*valp) { 286 F_SET(clp, CL_RENAME_OK); 287 288 /* 289 * If the screen is live, i.e. we're not reading the 290 * .exrc file, update the window. 291 */ 292 if (sp->frp != NULL && sp->frp->name != NULL) 293 (void)cl_rename(sp, sp->frp->name, 1); 294 } else { 295 F_CLR(clp, CL_RENAME_OK); 296 297 (void)cl_rename(sp, NULL, 0); 298 } 299 break; 300 } 301 return (0); 302 } 303 304 /* 305 * cl_omesg -- 306 * Turn the tty write permission on or off. 307 * 308 * PUBLIC: int cl_omesg __P((SCR *, CL_PRIVATE *, int)); 309 */ 310 int 311 cl_omesg(SCR *sp, CL_PRIVATE *clp, int on) 312 { 313 struct stat sb; 314 char *tty; 315 316 /* Find the tty, get the current permissions. */ 317 if ((tty = ttyname(STDERR_FILENO)) == NULL) { 318 if (sp != NULL) 319 msgq(sp, M_SYSERR, "stderr"); 320 return (1); 321 } 322 if (stat(tty, &sb) < 0) { 323 if (sp != NULL) 324 msgq(sp, M_SYSERR, "%s", tty); 325 return (1); 326 } 327 328 /* Save the original status if it's unknown. */ 329 if (clp->tgw == TGW_UNKNOWN) 330 clp->tgw = sb.st_mode & S_IWGRP ? TGW_SET : TGW_UNSET; 331 332 /* Toggle the permissions. */ 333 if (on) { 334 if (chmod(tty, sb.st_mode | S_IWGRP) < 0) { 335 if (sp != NULL) 336 msgq(sp, M_SYSERR, 337 "046|messages not turned on: %s", tty); 338 return (1); 339 } 340 } else 341 if (chmod(tty, sb.st_mode & ~S_IWGRP) < 0) { 342 if (sp != NULL) 343 msgq(sp, M_SYSERR, 344 "045|messages not turned off: %s", tty); 345 return (1); 346 } 347 return (0); 348 } 349 350 /* 351 * cl_ssize -- 352 * Return the terminal size. 353 * 354 * PUBLIC: int cl_ssize __P((SCR *, int, size_t *, size_t *, int *)); 355 */ 356 int 357 cl_ssize(SCR *sp, int sigwinch, size_t *rowp, size_t *colp, int *changedp) 358 { 359 struct winsize win; 360 size_t col, row; 361 int rval; 362 char *p; 363 364 /* Assume it's changed. */ 365 if (changedp != NULL) 366 *changedp = 1; 367 368 /* 369 * !!! 370 * sp may be NULL. 371 * 372 * Get the screen rows and columns. If the values are wrong, it's 373 * not a big deal -- as soon as the user sets them explicitly the 374 * environment will be set and the screen package will use the new 375 * values. 376 * 377 * Try TIOCGWINSZ. 378 */ 379 row = col = 0; 380 if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1) { 381 row = win.ws_row; 382 col = win.ws_col; 383 } 384 /* If here because of suspend or a signal, only trust TIOCGWINSZ. */ 385 if (sigwinch) { 386 /* 387 * Somebody didn't get TIOCGWINSZ right, or has suspend 388 * without window resizing support. The user just lost, 389 * but there's nothing we can do. 390 */ 391 if (row == 0 || col == 0) { 392 if (changedp != NULL) 393 *changedp = 0; 394 return (0); 395 } 396 397 /* 398 * SunOS systems deliver SIGWINCH when windows are uncovered 399 * as well as when they change size. In addition, we call 400 * here when continuing after being suspended since the window 401 * may have changed size. Since we don't want to background 402 * all of the screens just because the window was uncovered, 403 * ignore the signal if there's no change. 404 */ 405 if (sp != NULL && 406 row == O_VAL(sp, O_LINES) && col == O_VAL(sp, O_COLUMNS)) { 407 if (changedp != NULL) 408 *changedp = 0; 409 return (0); 410 } 411 412 if (rowp != NULL) 413 *rowp = row; 414 if (colp != NULL) 415 *colp = col; 416 return (0); 417 } 418 419 /* 420 * !!! 421 * If TIOCGWINSZ failed, or had entries of 0, try termcap. This 422 * routine is called before any termcap or terminal information 423 * has been set up. If there's no TERM environmental variable set, 424 * let it go, at least ex can run. 425 */ 426 if (row == 0 || col == 0) { 427 if ((p = getenv("TERM")) == NULL) 428 goto noterm; 429 if (row == 0) 430 if ((rval = tigetnum("lines")) < 0) 431 msgq(sp, M_SYSERR, "tigetnum: lines"); 432 else 433 row = rval; 434 if (col == 0) 435 if ((rval = tigetnum("cols")) < 0) 436 msgq(sp, M_SYSERR, "tigetnum: cols"); 437 else 438 col = rval; 439 } 440 441 /* If nothing else, well, it's probably a VT100. */ 442 noterm: if (row == 0) 443 row = 24; 444 if (col == 0) 445 col = 80; 446 447 /* 448 * !!! 449 * POSIX 1003.2 requires the environment to override everything. 450 * Often, people can get nvi to stop messing up their screen by 451 * deleting the LINES and COLUMNS environment variables from their 452 * dot-files. 453 */ 454 if ((p = getenv("LINES")) != NULL) 455 row = strtol(p, NULL, 10); 456 if ((p = getenv("COLUMNS")) != NULL) 457 col = strtol(p, NULL, 10); 458 459 if (rowp != NULL) 460 *rowp = row; 461 if (colp != NULL) 462 *colp = col; 463 return (0); 464 } 465 466 /* 467 * cl_putchar -- 468 * Function version of putchar, for tputs. 469 * 470 * PUBLIC: int cl_putchar __P((int)); 471 */ 472 int 473 cl_putchar(int ch) 474 { 475 return (putchar(ch)); 476 } 477