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