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