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