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