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