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