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