119304Speter/*-
219304Speter * Copyright (c) 1993, 1994
319304Speter *	The Regents of the University of California.  All rights reserved.
419304Speter * Copyright (c) 1993, 1994, 1995, 1996
519304Speter *	Keith Bostic.  All rights reserved.
619304Speter *
719304Speter * See the LICENSE file for redistribution information.
819304Speter */
919304Speter
1019304Speter#include "config.h"
1119304Speter
1219304Speter#ifndef lint
13270026Semastestatic const char sccsid[] = "$Id: cl_term.c,v 10.34 2013/12/07 16:21:14 wjenkner Exp $";
1419304Speter#endif /* not lint */
1519304Speter
1619304Speter#include <sys/types.h>
1719304Speter#include <sys/ioctl.h>
1819304Speter#include <sys/queue.h>
1919304Speter#include <sys/stat.h>
2019304Speter
2119304Speter#include <bitstring.h>
2219304Speter#include <errno.h>
2319304Speter#include <limits.h>
2419304Speter#include <signal.h>
2519304Speter#include <stdio.h>
2619304Speter#include <stdlib.h>
2719304Speter#include <string.h>
28254225Speter#ifdef HAVE_TERM_H
29254225Speter#include <term.h>
30254225Speter#endif
3119304Speter#include <termios.h>
3219304Speter#include <unistd.h>
3319304Speter
3419304Speter#include "../common/common.h"
3519304Speter#include "cl.h"
3619304Speter
3719304Speterstatic int cl_pfmap __P((SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t));
3819304Speter
3919304Speter/*
4019304Speter * XXX
4119304Speter * THIS REQUIRES THAT ALL SCREENS SHARE A TERMINAL TYPE.
4219304Speter */
4319304Spetertypedef struct _tklist {
4419304Speter	char	*ts;			/* Key's termcap string. */
4519304Speter	char	*output;		/* Corresponding vi command. */
4619304Speter	char	*name;			/* Name. */
4719304Speter	u_char	 value;			/* Special value (for lookup). */
4819304Speter} TKLIST;
4919304Speterstatic TKLIST const c_tklist[] = {	/* Command mappings. */
5019304Speter	{"kil1",	"O",	"insert line"},
5119304Speter	{"kdch1",	"x",	"delete character"},
5219304Speter	{"kcud1",	"j",	"cursor down"},
5319304Speter	{"kel",		"D",	"delete to eol"},
5419304Speter	{"kind",     "\004",	"scroll down"},			/* ^D */
5519304Speter	{"kll",		"$",	"go to eol"},
5669482Sru	{"kend",	"$",	"go to eol"},
5719304Speter	{"khome",	"^",	"go to sol"},
5819304Speter	{"kich1",	"i",	"insert at cursor"},
5919304Speter	{"kdl1",       "dd",	"delete line"},
6019304Speter	{"kcub1",	"h",	"cursor left"},
6119304Speter	{"knp",	     "\006",	"page down"},			/* ^F */
6219304Speter	{"kpp",	     "\002",	"page up"},			/* ^B */
6319304Speter	{"kri",	     "\025",	"scroll up"},			/* ^U */
6419304Speter	{"ked",	       "dG",	"delete to end of screen"},
6519304Speter	{"kcuf1",	"l",	"cursor right"},
6619304Speter	{"kcuu1",	"k",	"cursor up"},
6719304Speter	{NULL},
6819304Speter};
6919304Speterstatic TKLIST const m1_tklist[] = {	/* Input mappings (lookup). */
7019304Speter	{NULL},
7119304Speter};
7219304Speterstatic TKLIST const m2_tklist[] = {	/* Input mappings (set or delete). */
7319304Speter	{"kcud1",  "\033ja",	"cursor down"},			/* ^[ja */
7419304Speter	{"kcub1",  "\033ha",	"cursor left"},			/* ^[ha */
7519304Speter	{"kcuu1",  "\033ka",	"cursor up"},			/* ^[ka */
7619304Speter	{"kcuf1",  "\033la",	"cursor right"},		/* ^[la */
7719304Speter	{NULL},
7819304Speter};
7919304Speter
8019304Speter/*
8119304Speter * cl_term_init --
8219304Speter *	Initialize the special keys defined by the termcap/terminfo entry.
8319304Speter *
8419304Speter * PUBLIC: int cl_term_init __P((SCR *));
8519304Speter */
8619304Speterint
87254225Spetercl_term_init(SCR *sp)
8819304Speter{
8919304Speter	KEYLIST *kp;
9019304Speter	SEQ *qp;
9119304Speter	TKLIST const *tkp;
9219304Speter	char *t;
93254225Speter	CHAR_T name[60];
94254225Speter	CHAR_T output[5];
95254225Speter	CHAR_T ts[20];
96254225Speter	CHAR_T *wp;
97254225Speter	size_t wlen;
9819304Speter
9919304Speter	/* Command mappings. */
10019304Speter	for (tkp = c_tklist; tkp->name != NULL; ++tkp) {
10119304Speter		if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
10219304Speter			continue;
103254225Speter		CHAR2INT(sp, tkp->name, strlen(tkp->name), wp, wlen);
104254225Speter		MEMCPY(name, wp, wlen);
105254225Speter		CHAR2INT(sp, t, strlen(t), wp, wlen);
106254225Speter		MEMCPY(ts, wp, wlen);
107254225Speter		CHAR2INT(sp, tkp->output, strlen(tkp->output), wp, wlen);
108254225Speter		MEMCPY(output, wp, wlen);
109254225Speter		if (seq_set(sp, name, strlen(tkp->name), ts, strlen(t),
110254225Speter		    output, strlen(tkp->output), SEQ_COMMAND,
11119304Speter		    SEQ_NOOVERWRITE | SEQ_SCREEN))
11219304Speter			return (1);
11319304Speter	}
11419304Speter
11519304Speter	/* Input mappings needing to be looked up. */
11619304Speter	for (tkp = m1_tklist; tkp->name != NULL; ++tkp) {
11719304Speter		if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
11819304Speter			continue;
11919304Speter		for (kp = keylist;; ++kp)
12019304Speter			if (kp->value == tkp->value)
12119304Speter				break;
12219304Speter		if (kp == NULL)
12319304Speter			continue;
124254225Speter		CHAR2INT(sp, tkp->name, strlen(tkp->name), wp, wlen);
125254225Speter		MEMCPY(name, wp, wlen);
126254225Speter		CHAR2INT(sp, t, strlen(t), wp, wlen);
127254225Speter		MEMCPY(ts, wp, wlen);
128254225Speter		output[0] = (UCHAR_T)kp->ch;
129254225Speter		if (seq_set(sp, name, strlen(tkp->name), ts, strlen(t),
130254225Speter		    output, 1, SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN))
13119304Speter			return (1);
13219304Speter	}
13319304Speter
13419304Speter	/* Input mappings that are already set or are text deletions. */
13519304Speter	for (tkp = m2_tklist; tkp->name != NULL; ++tkp) {
13619304Speter		if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
13719304Speter			continue;
13819304Speter		/*
13919304Speter		 * !!!
14019304Speter		 * Some terminals' <cursor_left> keys send single <backspace>
14119304Speter		 * characters.  This is okay in command mapping, but not okay
14219304Speter		 * in input mapping.  That combination is the only one we'll
14319304Speter		 * ever see, hopefully, so kluge it here for now.
14419304Speter		 */
14519304Speter		if (!strcmp(t, "\b"))
14619304Speter			continue;
14719304Speter		if (tkp->output == NULL) {
148254225Speter			CHAR2INT(sp, tkp->name, strlen(tkp->name), wp, wlen);
149254225Speter			MEMCPY(name, wp, wlen);
150254225Speter			CHAR2INT(sp, t, strlen(t), wp, wlen);
151254225Speter			MEMCPY(ts, wp, wlen);
152254225Speter			if (seq_set(sp, name, strlen(tkp->name),
153254225Speter			    ts, strlen(t), NULL, 0,
15419304Speter			    SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN))
15519304Speter				return (1);
156254225Speter		} else {
157254225Speter			CHAR2INT(sp, tkp->name, strlen(tkp->name), wp, wlen);
158254225Speter			MEMCPY(name, wp, wlen);
159254225Speter			CHAR2INT(sp, t, strlen(t), wp, wlen);
160254225Speter			MEMCPY(ts, wp, wlen);
161254225Speter			CHAR2INT(sp, tkp->output, strlen(tkp->output), wp, wlen);
162254225Speter			MEMCPY(output, wp, wlen);
163254225Speter			if (seq_set(sp, name, strlen(tkp->name),
164254225Speter			    ts, strlen(t), output, strlen(tkp->output),
16519304Speter			    SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN))
16619304Speter				return (1);
167254225Speter		}
16819304Speter	}
16919304Speter
17019304Speter	/*
17119304Speter	 * Rework any function key mappings that were set before the
17219304Speter	 * screen was initialized.
17319304Speter	 */
174254225Speter	SLIST_FOREACH(qp, sp->gp->seqq, q)
17519304Speter		if (F_ISSET(qp, SEQ_FUNCMAP))
17619304Speter			(void)cl_pfmap(sp, qp->stype,
17719304Speter			    qp->input, qp->ilen, qp->output, qp->olen);
17819304Speter	return (0);
17919304Speter}
18019304Speter
18119304Speter/*
18219304Speter * cl_term_end --
18319304Speter *	End the special keys defined by the termcap/terminfo entry.
18419304Speter *
18519304Speter * PUBLIC: int cl_term_end __P((GS *));
18619304Speter */
18719304Speterint
188254225Spetercl_term_end(GS *gp)
18919304Speter{
190270026Semaste	SEQ *qp, *nqp, *pre_qp = NULL;
19119304Speter
19219304Speter	/* Delete screen specific mappings. */
193254225Speter	SLIST_FOREACH_SAFE(qp, gp->seqq, q, nqp)
194254225Speter		if (F_ISSET(qp, SEQ_SCREEN)) {
195270026Semaste			if (qp == SLIST_FIRST(gp->seqq))
196270026Semaste				SLIST_REMOVE_HEAD(gp->seqq, q);
197270026Semaste			else
198270026Semaste				SLIST_REMOVE_AFTER(pre_qp, q);
199254225Speter			(void)seq_free(qp);
200270026Semaste		} else
201270026Semaste			pre_qp = qp;
20219304Speter	return (0);
20319304Speter}
20419304Speter
20519304Speter/*
20619304Speter * cl_fmap --
20719304Speter *	Map a function key.
20819304Speter *
20919304Speter * PUBLIC: int cl_fmap __P((SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t));
21019304Speter */
21119304Speterint
212254225Spetercl_fmap(SCR *sp, seq_t stype, CHAR_T *from, size_t flen, CHAR_T *to, size_t tlen)
21319304Speter{
21419304Speter	/* Ignore until the screen is running, do the real work then. */
21519304Speter	if (F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_SCR_VI))
21619304Speter		return (0);
21719304Speter	if (F_ISSET(sp, SC_EX) && !F_ISSET(sp, SC_SCR_EX))
21819304Speter		return (0);
21919304Speter
22019304Speter	return (cl_pfmap(sp, stype, from, flen, to, tlen));
22119304Speter}
22219304Speter
22319304Speter/*
22419304Speter * cl_pfmap --
22519304Speter *	Map a function key (private version).
22619304Speter */
22719304Speterstatic int
228254225Spetercl_pfmap(SCR *sp, seq_t stype, CHAR_T *from, size_t flen, CHAR_T *to, size_t tlen)
22919304Speter{
23019304Speter	size_t nlen;
231254225Speter	char *p;
232254225Speter	char name[64];
233254225Speter	CHAR_T keyname[64];
234254225Speter	CHAR_T ts[20];
235254225Speter	CHAR_T *wp;
236254225Speter	size_t wlen;
23719304Speter
238254225Speter	(void)snprintf(name, sizeof(name), "kf%d",
239254225Speter			(int)STRTOL(from+1,NULL,10));
240254225Speter	if ((p = tigetstr(name)) == NULL ||
24119304Speter	    p == (char *)-1 || strlen(p) == 0)
24219304Speter		p = NULL;
24319304Speter	if (p == NULL) {
244254225Speter		msgq_wstr(sp, M_ERR, from, "233|This terminal has no %s key");
24519304Speter		return (1);
24619304Speter	}
24719304Speter
248254225Speter	nlen = SPRINTF(keyname,
249254225Speter	    SIZE(keyname), L("function key %d"),
250254225Speter			(int)STRTOL(from+1,NULL,10));
251254225Speter	CHAR2INT(sp, p, strlen(p), wp, wlen);
252254225Speter	MEMCPY(ts, wp, wlen);
25319304Speter	return (seq_set(sp, keyname, nlen,
254254225Speter	    ts, strlen(p), to, tlen, stype, SEQ_NOOVERWRITE | SEQ_SCREEN));
25519304Speter}
25619304Speter
25719304Speter/*
25819304Speter * cl_optchange --
25919304Speter *	Curses screen specific "option changed" routine.
26019304Speter *
26119304Speter * PUBLIC: int cl_optchange __P((SCR *, int, char *, u_long *));
26219304Speter */
26319304Speterint
264254225Spetercl_optchange(SCR *sp, int opt, char *str, u_long *valp)
26519304Speter{
26619304Speter	CL_PRIVATE *clp;
26719304Speter
26819304Speter	clp = CLP(sp);
26919304Speter
27019304Speter	switch (opt) {
27119304Speter	case O_COLUMNS:
27219304Speter	case O_LINES:
27319304Speter	case O_TERM:
27419304Speter		/*
27519304Speter		 * Changing the columns, lines or terminal require that
27619304Speter		 * we restart the screen.
27719304Speter		 */
27819304Speter		F_SET(sp->gp, G_SRESTART);
27919304Speter		F_CLR(sp, SC_SCR_EX | SC_SCR_VI);
28019304Speter		break;
28119304Speter	case O_MESG:
282254225Speter		(void)cl_omesg(sp, clp, *valp);
28319304Speter		break;
28419304Speter	case O_WINDOWNAME:
28519304Speter		if (*valp) {
28619304Speter			F_SET(clp, CL_RENAME_OK);
28719304Speter
28819304Speter			/*
28919304Speter			 * If the screen is live, i.e. we're not reading the
29019304Speter			 * .exrc file, update the window.
29119304Speter			 */
29219304Speter			if (sp->frp != NULL && sp->frp->name != NULL)
29319304Speter				(void)cl_rename(sp, sp->frp->name, 1);
294254225Speter		} else {
295254225Speter			F_CLR(clp, CL_RENAME_OK);
296254225Speter
297254225Speter			(void)cl_rename(sp, NULL, 0);
29819304Speter		}
29919304Speter		break;
30019304Speter	}
30119304Speter	return (0);
30219304Speter}
30319304Speter
30419304Speter/*
30519304Speter * cl_omesg --
30619304Speter *	Turn the tty write permission on or off.
30719304Speter *
30819304Speter * PUBLIC: int cl_omesg __P((SCR *, CL_PRIVATE *, int));
30919304Speter */
31019304Speterint
311254225Spetercl_omesg(SCR *sp, CL_PRIVATE *clp, int on)
31219304Speter{
31319304Speter	struct stat sb;
31419304Speter	char *tty;
31519304Speter
31619304Speter	/* Find the tty, get the current permissions. */
31719304Speter	if ((tty = ttyname(STDERR_FILENO)) == NULL) {
31819304Speter		if (sp != NULL)
31919304Speter			msgq(sp, M_SYSERR, "stderr");
32019304Speter		return (1);
32119304Speter	}
32219304Speter	if (stat(tty, &sb) < 0) {
32319304Speter		if (sp != NULL)
32419304Speter			msgq(sp, M_SYSERR, "%s", tty);
32519304Speter		return (1);
32619304Speter	}
32719304Speter
32819304Speter	/* Save the original status if it's unknown. */
32919304Speter	if (clp->tgw == TGW_UNKNOWN)
33019304Speter		clp->tgw = sb.st_mode & S_IWGRP ? TGW_SET : TGW_UNSET;
33119304Speter
33219304Speter	/* Toggle the permissions. */
33319304Speter	if (on) {
33419304Speter		if (chmod(tty, sb.st_mode | S_IWGRP) < 0) {
33519304Speter			if (sp != NULL)
33619304Speter				msgq(sp, M_SYSERR,
33719304Speter				    "046|messages not turned on: %s", tty);
33819304Speter			return (1);
33919304Speter		}
34019304Speter	} else
34119304Speter		if (chmod(tty, sb.st_mode & ~S_IWGRP) < 0) {
34219304Speter			if (sp != NULL)
34319304Speter				msgq(sp, M_SYSERR,
34419304Speter				    "045|messages not turned off: %s", tty);
34519304Speter			return (1);
34619304Speter		}
34719304Speter	return (0);
34819304Speter}
34919304Speter
35019304Speter/*
35119304Speter * cl_ssize --
35219304Speter *	Return the terminal size.
35319304Speter *
35419304Speter * PUBLIC: int cl_ssize __P((SCR *, int, size_t *, size_t *, int *));
35519304Speter */
35619304Speterint
357254225Spetercl_ssize(SCR *sp, int sigwinch, size_t *rowp, size_t *colp, int *changedp)
35819304Speter{
35919304Speter	struct winsize win;
36019304Speter	size_t col, row;
36119304Speter	int rval;
36219304Speter	char *p;
36319304Speter
36419304Speter	/* Assume it's changed. */
36519304Speter	if (changedp != NULL)
36619304Speter		*changedp = 1;
36719304Speter
36819304Speter	/*
36919304Speter	 * !!!
37019304Speter	 * sp may be NULL.
37119304Speter	 *
37219304Speter	 * Get the screen rows and columns.  If the values are wrong, it's
37319304Speter	 * not a big deal -- as soon as the user sets them explicitly the
37419304Speter	 * environment will be set and the screen package will use the new
37519304Speter	 * values.
37619304Speter	 *
37719304Speter	 * Try TIOCGWINSZ.
37819304Speter	 */
37919304Speter	row = col = 0;
38019304Speter	if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1) {
38119304Speter		row = win.ws_row;
38219304Speter		col = win.ws_col;
38319304Speter	}
38419304Speter	/* If here because of suspend or a signal, only trust TIOCGWINSZ. */
38519304Speter	if (sigwinch) {
38619304Speter		/*
38719304Speter		 * Somebody didn't get TIOCGWINSZ right, or has suspend
38819304Speter		 * without window resizing support.  The user just lost,
38919304Speter		 * but there's nothing we can do.
39019304Speter		 */
39119304Speter		if (row == 0 || col == 0) {
39219304Speter			if (changedp != NULL)
39319304Speter				*changedp = 0;
39419304Speter			return (0);
39519304Speter		}
39619304Speter
39719304Speter		/*
39819304Speter		 * SunOS systems deliver SIGWINCH when windows are uncovered
39919304Speter		 * as well as when they change size.  In addition, we call
40019304Speter		 * here when continuing after being suspended since the window
40119304Speter		 * may have changed size.  Since we don't want to background
40219304Speter		 * all of the screens just because the window was uncovered,
40319304Speter		 * ignore the signal if there's no change.
40419304Speter		 */
40519304Speter		if (sp != NULL &&
40619304Speter		    row == O_VAL(sp, O_LINES) && col == O_VAL(sp, O_COLUMNS)) {
40719304Speter			if (changedp != NULL)
40819304Speter				*changedp = 0;
40919304Speter			return (0);
41019304Speter		}
41119304Speter
41219304Speter		if (rowp != NULL)
41319304Speter			*rowp = row;
41419304Speter		if (colp != NULL)
41519304Speter			*colp = col;
41619304Speter		return (0);
41719304Speter	}
41819304Speter
41919304Speter	/*
42019304Speter	 * !!!
42119304Speter	 * If TIOCGWINSZ failed, or had entries of 0, try termcap.  This
42219304Speter	 * routine is called before any termcap or terminal information
42319304Speter	 * has been set up.  If there's no TERM environmental variable set,
42419304Speter	 * let it go, at least ex can run.
42519304Speter	 */
42619304Speter	if (row == 0 || col == 0) {
42719304Speter		if ((p = getenv("TERM")) == NULL)
42819304Speter			goto noterm;
42919304Speter		if (row == 0)
43019304Speter			if ((rval = tigetnum("lines")) < 0)
43119304Speter				msgq(sp, M_SYSERR, "tigetnum: lines");
43219304Speter			else
43319304Speter				row = rval;
43419304Speter		if (col == 0)
43519304Speter			if ((rval = tigetnum("cols")) < 0)
43619304Speter				msgq(sp, M_SYSERR, "tigetnum: cols");
43719304Speter			else
43819304Speter				col = rval;
43919304Speter	}
44019304Speter
44119304Speter	/* If nothing else, well, it's probably a VT100. */
44219304Speternoterm:	if (row == 0)
44319304Speter		row = 24;
44419304Speter	if (col == 0)
44519304Speter		col = 80;
44619304Speter
44719304Speter	/*
44819304Speter	 * !!!
44919304Speter	 * POSIX 1003.2 requires the environment to override everything.
45019304Speter	 * Often, people can get nvi to stop messing up their screen by
45119304Speter	 * deleting the LINES and COLUMNS environment variables from their
45219304Speter	 * dot-files.
45319304Speter	 */
45419304Speter	if ((p = getenv("LINES")) != NULL)
45519304Speter		row = strtol(p, NULL, 10);
45619304Speter	if ((p = getenv("COLUMNS")) != NULL)
45719304Speter		col = strtol(p, NULL, 10);
45819304Speter
45919304Speter	if (rowp != NULL)
46019304Speter		*rowp = row;
46119304Speter	if (colp != NULL)
46219304Speter		*colp = col;
46319304Speter	return (0);
46419304Speter}
46519304Speter
46619304Speter/*
46719304Speter * cl_putchar --
46819304Speter *	Function version of putchar, for tputs.
46919304Speter *
47019304Speter * PUBLIC: int cl_putchar __P((int));
47119304Speter */
47219304Speterint
473254225Spetercl_putchar(int ch)
47419304Speter{
47519304Speter	return (putchar(ch));
47619304Speter}
477