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