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