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