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