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.33 2012/04/21 23:51:46 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 __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; 191 192 /* Delete screen specific mappings. */ 193 SLIST_FOREACH_SAFE(qp, gp->seqq, q, nqp) 194 if (F_ISSET(qp, SEQ_SCREEN)) { 195 SLIST_REMOVE_HEAD(gp->seqq, q); 196 (void)seq_free(qp); 197 } 198 return (0); 199 } 200 201 /* 202 * cl_fmap -- 203 * Map a function key. 204 * 205 * PUBLIC: int cl_fmap __P((SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t)); 206 */ 207 int 208 cl_fmap(SCR *sp, seq_t stype, CHAR_T *from, size_t flen, CHAR_T *to, size_t tlen) 209 { 210 /* Ignore until the screen is running, do the real work then. */ 211 if (F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_SCR_VI)) 212 return (0); 213 if (F_ISSET(sp, SC_EX) && !F_ISSET(sp, SC_SCR_EX)) 214 return (0); 215 216 return (cl_pfmap(sp, stype, from, flen, to, tlen)); 217 } 218 219 /* 220 * cl_pfmap -- 221 * Map a function key (private version). 222 */ 223 static int 224 cl_pfmap(SCR *sp, seq_t stype, CHAR_T *from, size_t flen, CHAR_T *to, size_t tlen) 225 { 226 size_t nlen; 227 char *p; 228 char name[64]; 229 CHAR_T keyname[64]; 230 CHAR_T ts[20]; 231 CHAR_T *wp; 232 size_t wlen; 233 234 (void)snprintf(name, sizeof(name), "kf%d", 235 (int)STRTOL(from+1,NULL,10)); 236 if ((p = tigetstr(name)) == NULL || 237 p == (char *)-1 || strlen(p) == 0) 238 p = NULL; 239 if (p == NULL) { 240 msgq_wstr(sp, M_ERR, from, "233|This terminal has no %s key"); 241 return (1); 242 } 243 244 nlen = SPRINTF(keyname, 245 SIZE(keyname), L("function key %d"), 246 (int)STRTOL(from+1,NULL,10)); 247 CHAR2INT(sp, p, strlen(p), wp, wlen); 248 MEMCPY(ts, wp, wlen); 249 return (seq_set(sp, keyname, nlen, 250 ts, strlen(p), to, tlen, stype, SEQ_NOOVERWRITE | SEQ_SCREEN)); 251 } 252 253 /* 254 * cl_optchange -- 255 * Curses screen specific "option changed" routine. 256 * 257 * PUBLIC: int cl_optchange __P((SCR *, int, char *, u_long *)); 258 */ 259 int 260 cl_optchange(SCR *sp, int opt, char *str, u_long *valp) 261 { 262 CL_PRIVATE *clp; 263 264 clp = CLP(sp); 265 266 switch (opt) { 267 case O_COLUMNS: 268 case O_LINES: 269 case O_TERM: 270 /* 271 * Changing the columns, lines or terminal require that 272 * we restart the screen. 273 */ 274 F_SET(sp->gp, G_SRESTART); 275 F_CLR(sp, SC_SCR_EX | SC_SCR_VI); 276 break; 277 case O_MESG: 278 (void)cl_omesg(sp, clp, *valp); 279 break; 280 case O_WINDOWNAME: 281 if (*valp) { 282 F_SET(clp, CL_RENAME_OK); 283 284 /* 285 * If the screen is live, i.e. we're not reading the 286 * .exrc file, update the window. 287 */ 288 if (sp->frp != NULL && sp->frp->name != NULL) 289 (void)cl_rename(sp, sp->frp->name, 1); 290 } else { 291 F_CLR(clp, CL_RENAME_OK); 292 293 (void)cl_rename(sp, NULL, 0); 294 } 295 break; 296 } 297 return (0); 298 } 299 300 /* 301 * cl_omesg -- 302 * Turn the tty write permission on or off. 303 * 304 * PUBLIC: int cl_omesg __P((SCR *, CL_PRIVATE *, int)); 305 */ 306 int 307 cl_omesg(SCR *sp, CL_PRIVATE *clp, int on) 308 { 309 struct stat sb; 310 char *tty; 311 312 /* Find the tty, get the current permissions. */ 313 if ((tty = ttyname(STDERR_FILENO)) == NULL) { 314 if (sp != NULL) 315 msgq(sp, M_SYSERR, "stderr"); 316 return (1); 317 } 318 if (stat(tty, &sb) < 0) { 319 if (sp != NULL) 320 msgq(sp, M_SYSERR, "%s", tty); 321 return (1); 322 } 323 324 /* Save the original status if it's unknown. */ 325 if (clp->tgw == TGW_UNKNOWN) 326 clp->tgw = sb.st_mode & S_IWGRP ? TGW_SET : TGW_UNSET; 327 328 /* Toggle the permissions. */ 329 if (on) { 330 if (chmod(tty, sb.st_mode | S_IWGRP) < 0) { 331 if (sp != NULL) 332 msgq(sp, M_SYSERR, 333 "046|messages not turned on: %s", tty); 334 return (1); 335 } 336 } else 337 if (chmod(tty, sb.st_mode & ~S_IWGRP) < 0) { 338 if (sp != NULL) 339 msgq(sp, M_SYSERR, 340 "045|messages not turned off: %s", tty); 341 return (1); 342 } 343 return (0); 344 } 345 346 /* 347 * cl_ssize -- 348 * Return the terminal size. 349 * 350 * PUBLIC: int cl_ssize __P((SCR *, int, size_t *, size_t *, int *)); 351 */ 352 int 353 cl_ssize(SCR *sp, int sigwinch, size_t *rowp, size_t *colp, int *changedp) 354 { 355 struct winsize win; 356 size_t col, row; 357 int rval; 358 char *p; 359 360 /* Assume it's changed. */ 361 if (changedp != NULL) 362 *changedp = 1; 363 364 /* 365 * !!! 366 * sp may be NULL. 367 * 368 * Get the screen rows and columns. If the values are wrong, it's 369 * not a big deal -- as soon as the user sets them explicitly the 370 * environment will be set and the screen package will use the new 371 * values. 372 * 373 * Try TIOCGWINSZ. 374 */ 375 row = col = 0; 376 if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1) { 377 row = win.ws_row; 378 col = win.ws_col; 379 } 380 /* If here because of suspend or a signal, only trust TIOCGWINSZ. */ 381 if (sigwinch) { 382 /* 383 * Somebody didn't get TIOCGWINSZ right, or has suspend 384 * without window resizing support. The user just lost, 385 * but there's nothing we can do. 386 */ 387 if (row == 0 || col == 0) { 388 if (changedp != NULL) 389 *changedp = 0; 390 return (0); 391 } 392 393 /* 394 * SunOS systems deliver SIGWINCH when windows are uncovered 395 * as well as when they change size. In addition, we call 396 * here when continuing after being suspended since the window 397 * may have changed size. Since we don't want to background 398 * all of the screens just because the window was uncovered, 399 * ignore the signal if there's no change. 400 */ 401 if (sp != NULL && 402 row == O_VAL(sp, O_LINES) && col == O_VAL(sp, O_COLUMNS)) { 403 if (changedp != NULL) 404 *changedp = 0; 405 return (0); 406 } 407 408 if (rowp != NULL) 409 *rowp = row; 410 if (colp != NULL) 411 *colp = col; 412 return (0); 413 } 414 415 /* 416 * !!! 417 * If TIOCGWINSZ failed, or had entries of 0, try termcap. This 418 * routine is called before any termcap or terminal information 419 * has been set up. If there's no TERM environmental variable set, 420 * let it go, at least ex can run. 421 */ 422 if (row == 0 || col == 0) { 423 if ((p = getenv("TERM")) == NULL) 424 goto noterm; 425 if (row == 0) 426 if ((rval = tigetnum("lines")) < 0) 427 msgq(sp, M_SYSERR, "tigetnum: lines"); 428 else 429 row = rval; 430 if (col == 0) 431 if ((rval = tigetnum("cols")) < 0) 432 msgq(sp, M_SYSERR, "tigetnum: cols"); 433 else 434 col = rval; 435 } 436 437 /* If nothing else, well, it's probably a VT100. */ 438 noterm: if (row == 0) 439 row = 24; 440 if (col == 0) 441 col = 80; 442 443 /* 444 * !!! 445 * POSIX 1003.2 requires the environment to override everything. 446 * Often, people can get nvi to stop messing up their screen by 447 * deleting the LINES and COLUMNS environment variables from their 448 * dot-files. 449 */ 450 if ((p = getenv("LINES")) != NULL) 451 row = strtol(p, NULL, 10); 452 if ((p = getenv("COLUMNS")) != NULL) 453 col = strtol(p, NULL, 10); 454 455 if (rowp != NULL) 456 *rowp = row; 457 if (colp != NULL) 458 *colp = col; 459 return (0); 460 } 461 462 /* 463 * cl_putchar -- 464 * Function version of putchar, for tputs. 465 * 466 * PUBLIC: int cl_putchar __P((int)); 467 */ 468 int 469 cl_putchar(int ch) 470 { 471 return (putchar(ch)); 472 } 473