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