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
1319304Speterstatic const char sccsid[] = "@(#)cl_term.c	10.22 (Berkeley) 9/15/96";
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 <curses.h>
2319304Speter#include <errno.h>
2419304Speter#include <limits.h>
2519304Speter#include <signal.h>
2619304Speter#include <stdio.h>
2719304Speter#include <stdlib.h>
2819304Speter#include <string.h>
2919304Speter#include <termios.h>
3019304Speter#include <unistd.h>
3119304Speter
3219304Speter#include "../common/common.h"
3319304Speter#include "cl.h"
3419304Speter
3519304Speterstatic int cl_pfmap __P((SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t));
3619304Speter
3719304Speter/*
3819304Speter * XXX
3919304Speter * THIS REQUIRES THAT ALL SCREENS SHARE A TERMINAL TYPE.
4019304Speter */
4119304Spetertypedef struct _tklist {
4219304Speter	char	*ts;			/* Key's termcap string. */
4319304Speter	char	*output;		/* Corresponding vi command. */
4419304Speter	char	*name;			/* Name. */
4519304Speter	u_char	 value;			/* Special value (for lookup). */
4619304Speter} TKLIST;
4719304Speterstatic TKLIST const c_tklist[] = {	/* Command mappings. */
4819304Speter	{"kil1",	"O",	"insert line"},
4919304Speter	{"kdch1",	"x",	"delete character"},
5019304Speter	{"kcud1",	"j",	"cursor down"},
5119304Speter	{"kel",		"D",	"delete to eol"},
5219304Speter	{"kind",     "\004",	"scroll down"},			/* ^D */
5319304Speter	{"kll",		"$",	"go to eol"},
5469482Sru	{"kend",	"$",	"go to eol"},
5519304Speter	{"khome",	"^",	"go to sol"},
5619304Speter	{"kich1",	"i",	"insert at cursor"},
5719304Speter	{"kdl1",       "dd",	"delete line"},
5819304Speter	{"kcub1",	"h",	"cursor left"},
5919304Speter	{"knp",	     "\006",	"page down"},			/* ^F */
6019304Speter	{"kpp",	     "\002",	"page up"},			/* ^B */
6119304Speter	{"kri",	     "\025",	"scroll up"},			/* ^U */
6219304Speter	{"ked",	       "dG",	"delete to end of screen"},
6319304Speter	{"kcuf1",	"l",	"cursor right"},
6419304Speter	{"kcuu1",	"k",	"cursor up"},
6519304Speter	{NULL},
6619304Speter};
6719304Speterstatic TKLIST const m1_tklist[] = {	/* Input mappings (lookup). */
6819304Speter	{NULL},
6919304Speter};
7019304Speterstatic TKLIST const m2_tklist[] = {	/* Input mappings (set or delete). */
7119304Speter	{"kcud1",  "\033ja",	"cursor down"},			/* ^[ja */
7219304Speter	{"kcub1",  "\033ha",	"cursor left"},			/* ^[ha */
7319304Speter	{"kcuu1",  "\033ka",	"cursor up"},			/* ^[ka */
7419304Speter	{"kcuf1",  "\033la",	"cursor right"},		/* ^[la */
7519304Speter	{NULL},
7619304Speter};
7719304Speter
7819304Speter/*
7919304Speter * cl_term_init --
8019304Speter *	Initialize the special keys defined by the termcap/terminfo entry.
8119304Speter *
8219304Speter * PUBLIC: int cl_term_init __P((SCR *));
8319304Speter */
8419304Speterint
8519304Spetercl_term_init(sp)
8619304Speter	SCR *sp;
8719304Speter{
8819304Speter	KEYLIST *kp;
8919304Speter	SEQ *qp;
9019304Speter	TKLIST const *tkp;
9119304Speter	char *t;
9219304Speter
9319304Speter	/* Command mappings. */
9419304Speter	for (tkp = c_tklist; tkp->name != NULL; ++tkp) {
9519304Speter		if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
9619304Speter			continue;
9719304Speter		if (seq_set(sp, tkp->name, strlen(tkp->name), t, strlen(t),
9819304Speter		    tkp->output, strlen(tkp->output), SEQ_COMMAND,
9919304Speter		    SEQ_NOOVERWRITE | SEQ_SCREEN))
10019304Speter			return (1);
10119304Speter	}
10219304Speter
10319304Speter	/* Input mappings needing to be looked up. */
10419304Speter	for (tkp = m1_tklist; tkp->name != NULL; ++tkp) {
10519304Speter		if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
10619304Speter			continue;
10719304Speter		for (kp = keylist;; ++kp)
10819304Speter			if (kp->value == tkp->value)
10919304Speter				break;
11019304Speter		if (kp == NULL)
11119304Speter			continue;
11219304Speter		if (seq_set(sp, tkp->name, strlen(tkp->name), t, strlen(t),
11319304Speter		    &kp->ch, 1, SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN))
11419304Speter			return (1);
11519304Speter	}
11619304Speter
11719304Speter	/* Input mappings that are already set or are text deletions. */
11819304Speter	for (tkp = m2_tklist; tkp->name != NULL; ++tkp) {
11919304Speter		if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
12019304Speter			continue;
12119304Speter		/*
12219304Speter		 * !!!
12319304Speter		 * Some terminals' <cursor_left> keys send single <backspace>
12419304Speter		 * characters.  This is okay in command mapping, but not okay
12519304Speter		 * in input mapping.  That combination is the only one we'll
12619304Speter		 * ever see, hopefully, so kluge it here for now.
12719304Speter		 */
12819304Speter		if (!strcmp(t, "\b"))
12919304Speter			continue;
13019304Speter		if (tkp->output == NULL) {
13119304Speter			if (seq_set(sp, tkp->name, strlen(tkp->name),
13219304Speter			    t, strlen(t), NULL, 0,
13319304Speter			    SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN))
13419304Speter				return (1);
13519304Speter		} else
13619304Speter			if (seq_set(sp, tkp->name, strlen(tkp->name),
13719304Speter			    t, strlen(t), tkp->output, strlen(tkp->output),
13819304Speter			    SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN))
13919304Speter				return (1);
14019304Speter	}
14119304Speter
14219304Speter	/*
14319304Speter	 * Rework any function key mappings that were set before the
14419304Speter	 * screen was initialized.
14519304Speter	 */
14619304Speter	for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next)
14719304Speter		if (F_ISSET(qp, SEQ_FUNCMAP))
14819304Speter			(void)cl_pfmap(sp, qp->stype,
14919304Speter			    qp->input, qp->ilen, qp->output, qp->olen);
15019304Speter	return (0);
15119304Speter}
15219304Speter
15319304Speter/*
15419304Speter * cl_term_end --
15519304Speter *	End the special keys defined by the termcap/terminfo entry.
15619304Speter *
15719304Speter * PUBLIC: int cl_term_end __P((GS *));
15819304Speter */
15919304Speterint
16019304Spetercl_term_end(gp)
16119304Speter	GS *gp;
16219304Speter{
16319304Speter	SEQ *qp, *nqp;
16419304Speter
16519304Speter	/* Delete screen specific mappings. */
16619304Speter	for (qp = gp->seqq.lh_first; qp != NULL; qp = nqp) {
16719304Speter		nqp = qp->q.le_next;
16819304Speter		if (F_ISSET(qp, SEQ_SCREEN))
16919304Speter			(void)seq_mdel(qp);
17019304Speter	}
17119304Speter	return (0);
17219304Speter}
17319304Speter
17419304Speter/*
17519304Speter * cl_fmap --
17619304Speter *	Map a function key.
17719304Speter *
17819304Speter * PUBLIC: int cl_fmap __P((SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t));
17919304Speter */
18019304Speterint
18119304Spetercl_fmap(sp, stype, from, flen, to, tlen)
18219304Speter	SCR *sp;
18319304Speter	seq_t stype;
18419304Speter	CHAR_T *from, *to;
18519304Speter	size_t flen, tlen;
18619304Speter{
18719304Speter	/* Ignore until the screen is running, do the real work then. */
18819304Speter	if (F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_SCR_VI))
18919304Speter		return (0);
19019304Speter	if (F_ISSET(sp, SC_EX) && !F_ISSET(sp, SC_SCR_EX))
19119304Speter		return (0);
19219304Speter
19319304Speter	return (cl_pfmap(sp, stype, from, flen, to, tlen));
19419304Speter}
19519304Speter
19619304Speter/*
19719304Speter * cl_pfmap --
19819304Speter *	Map a function key (private version).
19919304Speter */
20019304Speterstatic int
20119304Spetercl_pfmap(sp, stype, from, flen, to, tlen)
20219304Speter	SCR *sp;
20319304Speter	seq_t stype;
20419304Speter	CHAR_T *from, *to;
20519304Speter	size_t flen, tlen;
20619304Speter{
20719304Speter	size_t nlen;
20819304Speter	char *p, keyname[64];
20919304Speter
21019304Speter	(void)snprintf(keyname, sizeof(keyname), "kf%d", atoi(from + 1));
21119304Speter	if ((p = tigetstr(keyname)) == NULL ||
21219304Speter	    p == (char *)-1 || strlen(p) == 0)
21319304Speter		p = NULL;
21419304Speter	if (p == NULL) {
21519304Speter		msgq_str(sp, M_ERR, from, "233|This terminal has no %s key");
21619304Speter		return (1);
21719304Speter	}
21819304Speter
21919304Speter	nlen = snprintf(keyname,
22019304Speter	    sizeof(keyname), "function key %d", atoi(from + 1));
22119304Speter	return (seq_set(sp, keyname, nlen,
22219304Speter	    p, strlen(p), to, tlen, stype, SEQ_NOOVERWRITE | SEQ_SCREEN));
22319304Speter}
22419304Speter
22519304Speter/*
22619304Speter * cl_optchange --
22719304Speter *	Curses screen specific "option changed" routine.
22819304Speter *
22919304Speter * PUBLIC: int cl_optchange __P((SCR *, int, char *, u_long *));
23019304Speter */
23119304Speterint
23219304Spetercl_optchange(sp, opt, str, valp)
23319304Speter	SCR *sp;
23419304Speter	int opt;
23519304Speter	char *str;
23619304Speter	u_long *valp;
23719304Speter{
23819304Speter	CL_PRIVATE *clp;
23919304Speter
24019304Speter	clp = CLP(sp);
24119304Speter
24219304Speter	switch (opt) {
24319304Speter	case O_COLUMNS:
24419304Speter	case O_LINES:
24519304Speter	case O_TERM:
24619304Speter		/*
24719304Speter		 * Changing the columns, lines or terminal require that
24819304Speter		 * we restart the screen.
24919304Speter		 */
25019304Speter		F_SET(sp->gp, G_SRESTART);
25119304Speter		F_CLR(sp, SC_SCR_EX | SC_SCR_VI);
25219304Speter		break;
25319304Speter	case O_MESG:
25419304Speter		(void)cl_omesg(sp, clp, !*valp);
25519304Speter		break;
25619304Speter	case O_WINDOWNAME:
25719304Speter		if (*valp) {
25819304Speter			F_CLR(clp, CL_RENAME_OK);
25919304Speter
26019304Speter			(void)cl_rename(sp, NULL, 0);
26119304Speter		} else {
26219304Speter			F_SET(clp, CL_RENAME_OK);
26319304Speter
26419304Speter			/*
26519304Speter			 * If the screen is live, i.e. we're not reading the
26619304Speter			 * .exrc file, update the window.
26719304Speter			 */
26819304Speter			if (sp->frp != NULL && sp->frp->name != NULL)
26919304Speter				(void)cl_rename(sp, sp->frp->name, 1);
27019304Speter		}
27119304Speter		break;
27219304Speter	}
27319304Speter	return (0);
27419304Speter}
27519304Speter
27619304Speter/*
27719304Speter * cl_omesg --
27819304Speter *	Turn the tty write permission on or off.
27919304Speter *
28019304Speter * PUBLIC: int cl_omesg __P((SCR *, CL_PRIVATE *, int));
28119304Speter */
28219304Speterint
28319304Spetercl_omesg(sp, clp, on)
28419304Speter	SCR *sp;
28519304Speter	CL_PRIVATE *clp;
28619304Speter	int on;
28719304Speter{
28819304Speter	struct stat sb;
28919304Speter	char *tty;
29019304Speter
29119304Speter	/* Find the tty, get the current permissions. */
29219304Speter	if ((tty = ttyname(STDERR_FILENO)) == NULL) {
29319304Speter		if (sp != NULL)
29419304Speter			msgq(sp, M_SYSERR, "stderr");
29519304Speter		return (1);
29619304Speter	}
29719304Speter	if (stat(tty, &sb) < 0) {
29819304Speter		if (sp != NULL)
29919304Speter			msgq(sp, M_SYSERR, "%s", tty);
30019304Speter		return (1);
30119304Speter	}
30219304Speter
30319304Speter	/* Save the original status if it's unknown. */
30419304Speter	if (clp->tgw == TGW_UNKNOWN)
30519304Speter		clp->tgw = sb.st_mode & S_IWGRP ? TGW_SET : TGW_UNSET;
30619304Speter
30719304Speter	/* Toggle the permissions. */
30819304Speter	if (on) {
30919304Speter		if (chmod(tty, sb.st_mode | S_IWGRP) < 0) {
31019304Speter			if (sp != NULL)
31119304Speter				msgq(sp, M_SYSERR,
31219304Speter				    "046|messages not turned on: %s", tty);
31319304Speter			return (1);
31419304Speter		}
31519304Speter	} else
31619304Speter		if (chmod(tty, sb.st_mode & ~S_IWGRP) < 0) {
31719304Speter			if (sp != NULL)
31819304Speter				msgq(sp, M_SYSERR,
31919304Speter				    "045|messages not turned off: %s", tty);
32019304Speter			return (1);
32119304Speter		}
32219304Speter	return (0);
32319304Speter}
32419304Speter
32519304Speter/*
32619304Speter * cl_ssize --
32719304Speter *	Return the terminal size.
32819304Speter *
32919304Speter * PUBLIC: int cl_ssize __P((SCR *, int, size_t *, size_t *, int *));
33019304Speter */
33119304Speterint
33219304Spetercl_ssize(sp, sigwinch, rowp, colp, changedp)
33319304Speter	SCR *sp;
33419304Speter	int sigwinch;
33519304Speter	size_t *rowp, *colp;
33619304Speter	int *changedp;
33719304Speter{
33819304Speter#ifdef TIOCGWINSZ
33919304Speter	struct winsize win;
34019304Speter#endif
34119304Speter	size_t col, row;
34219304Speter	int rval;
34319304Speter	char *p;
34419304Speter
34519304Speter	/* Assume it's changed. */
34619304Speter	if (changedp != NULL)
34719304Speter		*changedp = 1;
34819304Speter
34919304Speter	/*
35019304Speter	 * !!!
35119304Speter	 * sp may be NULL.
35219304Speter	 *
35319304Speter	 * Get the screen rows and columns.  If the values are wrong, it's
35419304Speter	 * not a big deal -- as soon as the user sets them explicitly the
35519304Speter	 * environment will be set and the screen package will use the new
35619304Speter	 * values.
35719304Speter	 *
35819304Speter	 * Try TIOCGWINSZ.
35919304Speter	 */
36019304Speter	row = col = 0;
36119304Speter#ifdef TIOCGWINSZ
36219304Speter	if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1) {
36319304Speter		row = win.ws_row;
36419304Speter		col = win.ws_col;
36519304Speter	}
36619304Speter#endif
36719304Speter	/* If here because of suspend or a signal, only trust TIOCGWINSZ. */
36819304Speter	if (sigwinch) {
36919304Speter		/*
37019304Speter		 * Somebody didn't get TIOCGWINSZ right, or has suspend
37119304Speter		 * without window resizing support.  The user just lost,
37219304Speter		 * but there's nothing we can do.
37319304Speter		 */
37419304Speter		if (row == 0 || col == 0) {
37519304Speter			if (changedp != NULL)
37619304Speter				*changedp = 0;
37719304Speter			return (0);
37819304Speter		}
37919304Speter
38019304Speter		/*
38119304Speter		 * SunOS systems deliver SIGWINCH when windows are uncovered
38219304Speter		 * as well as when they change size.  In addition, we call
38319304Speter		 * here when continuing after being suspended since the window
38419304Speter		 * may have changed size.  Since we don't want to background
38519304Speter		 * all of the screens just because the window was uncovered,
38619304Speter		 * ignore the signal if there's no change.
38719304Speter		 */
38819304Speter		if (sp != NULL &&
38919304Speter		    row == O_VAL(sp, O_LINES) && col == O_VAL(sp, O_COLUMNS)) {
39019304Speter			if (changedp != NULL)
39119304Speter				*changedp = 0;
39219304Speter			return (0);
39319304Speter		}
39419304Speter
39519304Speter		if (rowp != NULL)
39619304Speter			*rowp = row;
39719304Speter		if (colp != NULL)
39819304Speter			*colp = col;
39919304Speter		return (0);
40019304Speter	}
40119304Speter
40219304Speter	/*
40319304Speter	 * !!!
40419304Speter	 * If TIOCGWINSZ failed, or had entries of 0, try termcap.  This
40519304Speter	 * routine is called before any termcap or terminal information
40619304Speter	 * has been set up.  If there's no TERM environmental variable set,
40719304Speter	 * let it go, at least ex can run.
40819304Speter	 */
40919304Speter	if (row == 0 || col == 0) {
41019304Speter		if ((p = getenv("TERM")) == NULL)
41119304Speter			goto noterm;
41219304Speter		if (row == 0)
41319304Speter			if ((rval = tigetnum("lines")) < 0)
41419304Speter				msgq(sp, M_SYSERR, "tigetnum: lines");
41519304Speter			else
41619304Speter				row = rval;
41719304Speter		if (col == 0)
41819304Speter			if ((rval = tigetnum("cols")) < 0)
41919304Speter				msgq(sp, M_SYSERR, "tigetnum: cols");
42019304Speter			else
42119304Speter				col = rval;
42219304Speter	}
42319304Speter
42419304Speter	/* If nothing else, well, it's probably a VT100. */
42519304Speternoterm:	if (row == 0)
42619304Speter		row = 24;
42719304Speter	if (col == 0)
42819304Speter		col = 80;
42919304Speter
43019304Speter	/*
43119304Speter	 * !!!
43219304Speter	 * POSIX 1003.2 requires the environment to override everything.
43319304Speter	 * Often, people can get nvi to stop messing up their screen by
43419304Speter	 * deleting the LINES and COLUMNS environment variables from their
43519304Speter	 * dot-files.
43619304Speter	 */
43719304Speter	if ((p = getenv("LINES")) != NULL)
43819304Speter		row = strtol(p, NULL, 10);
43919304Speter	if ((p = getenv("COLUMNS")) != NULL)
44019304Speter		col = strtol(p, NULL, 10);
44119304Speter
44219304Speter	if (rowp != NULL)
44319304Speter		*rowp = row;
44419304Speter	if (colp != NULL)
44519304Speter		*colp = col;
44619304Speter	return (0);
44719304Speter}
44819304Speter
44919304Speter/*
45019304Speter * cl_putchar --
45119304Speter *	Function version of putchar, for tputs.
45219304Speter *
45319304Speter * PUBLIC: int cl_putchar __P((int));
45419304Speter */
45519304Speterint
45619304Spetercl_putchar(ch)
45719304Speter	int ch;
45819304Speter{
45919304Speter	return (putchar(ch));
46019304Speter}
461